Сохранить как не работает для моего приложения NSPersistentDocument

У меня есть приложение OS X, которое использует подкласс NSPersistentDocument для хранения данных. Тип постоянного хранилища Core Data — SQLite. Я полагаюсь на стандартные команды меню, которые создаются при создании нового проекта для приложения на основе документов с Core Data в Xcode.

Это приложение существует уже около двух лет и постоянно развивается. Только сегодня я обнаружил, что "Сохранить как" не работает в моей последней версии. Поведение выглядит следующим образом:

  • Я создаю новый документ и ввожу некоторые данные.
  • Я сохраняю документ, закрываю его и снова открываю. Все хорошо. Затем я выбираю «Сохранить как», выбираю новое имя и место для файла. Он переименовывается, и я могу ввести еще некоторые данные.
  • Сохраняю документ, закрываю, заново открываю, а он совсем пустой.
  • Когда я открываю исходный документ, все изменения, которые должны быть в сохраненном документе, присутствуют.
  • Приложение не вылетает и не выдает никаких ошибок.

Я почти уверен, что когда-то он работал правильно. Я попробовал самую первую «официальную» версию своей программы, и поведение такое же (неправильное).

Редактировать: я создал новый проект Xcode того же типа, только с одним объектом Core Data. Поведение такое же. Единственная разница с моим приложением заключается в том, что новый проект автоматически использовал новую команду меню «Дублировать», которая была введена в 10.7 Lion, вместо «Сохранить как». Поэтому мне нужно нажать клавишу Option, чтобы выбрать «Сохранить как».

Я тестировал его только на 10.9.3, но на двух разных Маках. У кого-нибудь есть идеи, где посмотреть?

Изменить 2: похоже, это связано с конкретными учетными записями (моя учетная запись на двух компьютерах). На другом аккаунте работает корректно.


person Dirk    schedule 09.06.2014    source источник


Ответы (3)


Для нас ложное поведение возникает даже для двоичного хранилища (мы используем sqlite в нашем приложении). Поэтому я не думаю, что изменение, упомянутое в ответе DTS, является той же проблемой. (В настоящее время мы строим для 10.8-SDK). Кроме того, это происходит только на некоторых машинах, к счастью, теперь у нас есть доступ к машине, на которой она ломается.

Для недокументированного метода перезапись

- (BOOL)_writeSafelyToURL:(NSURL*)url ofType:(NSString*)type forSaveOperation:(NSSaveOperationType)operation forceTemporaryDirectory:(BOOL)forceTemporary error:(NSError **)error
{
    return [super _writeSafelyToURL:url ofType:type forSaveOperation:operation forceTemporaryDirectory:YES error:error];
}

всегда использовать YES для параметра forceTemporaryDirectory:-решил проблему на машине, где происходит ошибка. Не рекомендуется для реального использования, это было только при попытке найти причину проблемы.

Я также отправил вопрос в список рассылки какао-dev: http://lists.apple.com/archives/cocoa-dev/2014/Jun/msg00358.html, возможно, у кого-то есть дополнительная информация.

person Felix Franz    schedule 25.06.2014
comment
Я думаю, вы правы, когда говорите, что изменение, упомянутое DTS, является другой проблемой. У меня также есть проблемы с объектами Duplicate и NSDate (вылет программы после дублирования). Я обновлю вопрос соответственно. Но как мне преодолеть отсутствие видимого @interface для «NSPersistentDocument», объявляющего ошибку компилятора селектора _writeSafelyToURL:...? - person Dirk; 25.06.2014
comment
Я принял ваш ответ, так как он привел меня к правильному решению (см. мой ответ). Спасибо! - person Dirk; 25.06.2014

Мы наблюдаем аналогичную проблему, но только для некоторых пользователей — мы сравнили трассировку стека между двумя машинами — на одной это работает, а на другой — нет, и мы видим разницу. В рабочей версии метод writeSafely использует временный файл, а в нерабочей версии временный файл не используется.

Это различие, по-видимому, происходит в частном методе NSDocument, который по какой-то причине решает не использовать временную папку.

Мы обнаружили, что если мы переопределим этот закрытый метод и установим для параметра forceTemporaryFile значение YES, проблема исчезнет.

Мы не хотим переопределять этот частный метод, так как нам нужно отправить его в магазин приложений, но, возможно, это работает для вас?

person Grouchal    schedule 24.06.2014
comment
В настоящее время мне не нужно отправлять в App Store. Какой частный метод вы имеете в виду? И где я могу получить его оригинальную реализацию? Пожалуйста, посмотрите дополнение, которое я сделал к моему вопросу, может быть, есть подсказка для вас. - person Dirk; 24.06.2014
comment
Привет. В данный момент у меня нет под рукой кода, но если вы переопределите метод NSDocument, writeToURL:ofType:forSaveOperation:originalContentsURL:error: вы должны увидеть, что переданный originalContentsURL равен нулю. В этом проблема — чтобы исправить это, мы посмотрели на трассировку стека и отметили 33 вызова — тот, что непосредственно перед writeToURL, является дополнительным на машинах, на которых есть проблема. Чтобы исправить это, мы использовали первый приватный метод в трассировке стека. - person Grouchal; 24.06.2014
comment
Привет. В данный момент у меня нет кода, но если вы переопределите метод NSDocument writeToURL:ofType:forSaveOperation:originalContentsURL:error: и поместите точку останова, вы должны увидеть трассировку стека. Затем вы должны увидеть, что переданный originalContentsURL равен нулю. В этом проблема — чтобы исправить это, мы посмотрели на трассировку стека и отметили 33 вызова — тот, что непосредственно перед writeToURL, является дополнительным на машинах, на которых есть проблема. Чтобы исправить это, мы использовали первый закрытый метод в трассировке стека, который имеет параметр forceTemporaryFiel: position 31 в трассировке стека. - person Grouchal; 24.06.2014

своей публикацией Феликс Франц направил меня на правильный путь. в списке рассылки какао-dev: Проблема связана с определенными настройками ACL. Я добавил следующий код в свой класс NSPersistentDocument, чтобы переопределить writeToURL:ofType:forSaveOperation:originalContentsURL:error::

- (BOOL)writeToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName forSaveOperation:(NSSaveOperationType)saveOperation originalContentsURL:(NSURL *)absoluteOriginalContentsURL error:(NSError *__autoreleasing *)error {
    if ((saveOperation == NSSaveAsOperation)  && (absoluteOriginalContentsURL == nil)) {
        NSLog(@"---------- absoluteOriginalContentsURL == nil for NSSaveAsOperation ------------");
        return [super writeToURL:absoluteURL ofType:typeName forSaveOperation:saveOperation originalContentsURL:self.fileURL error:error];
    }
    return [super writeToURL:absoluteURL ofType:typeName forSaveOperation:saveOperation originalContentsURL:absoluteOriginalContentsURL error:error];
}

Очевидно, что он предоставляет методу super self.fileURL, когда absoluteOriginalContentsURL по какой-то причине равно нулю. Теперь «Сохранить как» работает для всех протестированных мной учетных записей.

person Dirk    schedule 25.06.2014