Рецепт для того, чтобы пакеты Cocoa NSDocument хорошо работали с svn?

У меня есть инструмент, который пишет документы в стиле пакета. Он реализован с использованием NSDocument и переопределяет следующие методы NSDocument:

- (NSFileWrapper *)fileWrapperOfType:(NSString *)typeName
                              error:(NSError **)outError;

- (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper
                    ofType:(NSString *)typeName
                     error:(NSError **)outError;

Все это прекрасно, за исключением случаев, когда я сохраняю документ, находящийся под контролем версий. (Каталоги .svn не сохраняются и т. д.)

Есть ли где-нибудь хороший рецепт, как заставить мои документы хорошо работать с svn?


person Kaelin Colclasure    schedule 24.08.2010    source источник


Ответы (2)


Я предполагаю, что ваш код работает, создавая новую оболочку файла каждый раз, когда вызывается -fileWrapperOfType:error:. Это удобно, но не учитывает любые дополнительные файлы, которые могут существовать на диске внутри пакета.

Итак, что если вы начнете с создания/использования файловой оболочки, которая ссылается на существующее содержимое на диске. Измените эту оболочку, чтобы она соответствовала текущему состоянию документа, и верните результат. Когда эта оболочка записывается на диск, файлы svn должны поддерживаться должным образом.

person Mike Abdullah    schedule 24.08.2010
comment
Хорошо, спасибо! Да, было немного хлопотно реорганизовать все, чтобы это заработало… Но как только вы узнаете требование, это действительно кусок пирога! - person Kaelin Colclasure; 25.08.2010

Вот мое решение, основанное на ответе Майка!

Мои пакеты документов представляют собой пакеты с обычной иерархической структурой… Итак, есть четыре каталога, которые я мутирую во время сохранения:

  1. Сохранение создает новый верхний уровень (My.bundle)
  2. Каталог Contents изменен (My.bundle/Contents)
  3. Каталог Resources изменен (My.bundle/Contents/Resources)
  4. Обновлены локализованные ресурсы (My.bundle/Contents/Resources/en.lproj)

Рецепт начинается с добавления слота изменяемого словаря к вашему классу документа, чтобы сохранить содержимое каждого из этих каталогов.

@interface LMDocument : NSDocument {
@private
    // All this is for preserving SCM artifacts across saves…
    NSMutableDictionary * bundleWrappers;
    NSMutableDictionary * contentsWrappers;
    NSMutableDictionary * resourcesWrappers;
    NSMutableDictionary * localizedWrappers;
}

При создании нового документа они начинаются как пустые словари.

- (id)init;
{
    if ((self = [super init]) != nil) {
        bundleWrappers = [[NSMutableDictionary alloc] initWithCapacity:0];
        contentsWrappers = [[NSMutableDictionary alloc] initWithCapacity:0];
        resourcesWrappers = [[NSMutableDictionary alloc] initWithCapacity:0];
        localizedWrappers = [[NSMutableDictionary alloc] initWithCapacity:0];
    }
    return self;
}

При чтении в существующем документе замените их изменяемыми копиями соответствующего содержимого fileWrapper.

- (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper
                     ofType:(NSString *)typeName
                      error:(NSError **)outError;
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    bundleWrappers = [[fileWrapper fileWrappers] mutableCopy];
    contentsWrappers = [[[bundleWrappers objectForKey:@"Contents"] fileWrappers] mutableCopy];
    resourcesWrappers = [[[contentsWrappers objectForKey:@"Resources"] fileWrappers] mutableCopy];
    localizedWrappers = [[[resourcesWrappers objectForKey:@"en.lproj"] fileWrappers] mutableCopy];
    NSFileWrapper * infoPlistWrapper = [contentsWrappers objectForKey:@"Info.plist"];
    [contentsWrappers removeObjectForKey:@"Info.plist"]; // Replaced during save…
    // …
    NSMutableDictionary * localizedWrappersCopy = [localizedWrappers mutableCopy];
    [localizedWrappers enumerateKeysAndObjectsUsingBlock:^(id key,
                                                           id obj,
                                                           BOOL * stop)
     {
         if (mumble) { // If it's a file that will be replaced during save…
             [localizedWrappersCopy removeObjectForKey:key]; // Replaced during save…
             // …
         }
     }];
    localizedWrappers = localizedWrappersCopy;
    [pool drain];
    return YES;
}

И наконец, при сохранении документа используйте так тщательно подготовленные словари.

- (NSFileWrapper *)fileWrapperOfType:(NSString *)typeName
                               error:(NSError **)outError;
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    NSFileWrapper * localizedWrapper =
    [[NSFileWrapper alloc] initDirectoryWithFileWrappers:localizedWrappers];
    [resourcesWrappers setObject:localizedWrapper
                          forKey:@"en.lproj"];
    NSFileWrapper * resourcesWrapper =
    [[NSFileWrapper alloc] initDirectoryWithFileWrappers:resourcesWrappers];
    [contentsWrappers setObject:resourcesWrapper
                         forKey:@"Resources"];
    NSFileWrapper * contentsWrapper =
    [[NSFileWrapper alloc] initDirectoryWithFileWrappers:contentsWrappers];
    // …
    for (id item in mumble) {
        NSString * filename = [item filename];
        NSData * data = [item data];
        [localizedWrapper addRegularFileWithContents:data
                                   preferredFilename:filename];
    }
    [contentsWrapper addRegularFileWithContents:[self infoPlistData]
                              preferredFilename:@"Info.plist"];
    [pool drain];
    [bundleWrappers setObject:contentsWrapper
                       forKey:@"Contents"];
    NSFileWrapper * bundleWrapper =
    [[[NSFileWrapper alloc] initDirectoryWithFileWrappers:bundleWrappers] autorelease];
    return bundleWrapper;
}

Теперь, когда документ пакета редактируется, приложение сохраняет все файлы, которые оно не добавило в пакет, включая артефакты SCM и «другие» локализации!

person Kaelin Colclasure    schedule 25.08.2010