1

Core Data ドキュメントベースのアプリケーションが「名前を付けて保存」時にクラッシュします。この問題は、cocoa-dev スレッド「NSPersistentDocument objects "gutted" after Duplicate, Rename in 10.9」で説明されている問題と似ているようです。

主な違いは次のとおりです。

  • OS X 10.10 Yosemite をターゲットにして実行しています
  • 複製ではなく「名前を付けて保存」を使用する
  • クラッシュは以前に発生します。MOC 保存中

この問題は、最も単純な NSPersistentDocument にも影響します。少なくとも 2014 年から存在しています。したがって、他の人が同じ問題に遭遇し、共有したい回避策があることを願っています。

私のサンプル プロジェクトでは、単一の属性を持つ単一のエンティティを使用します。エンティティのすべてのインスタンスを表示するテーブル ビューと、新しいインスタンスを作成するためのボタンがあります。autosavesInPlace を無効にするためだけに、デフォルトのテンプレートから逸脱しました。

クラッシュを再現する手順は次のとおりです。

  1. Yosemite でビルドして実行します。このバグは El Capitan で修正されたようです
  2. 新しいドキュメントを作成する
  3. 新しいオブジェクトを挿入する
  4. ドキュメントを保存する
  5. ドキュメントを閉じる
  6. ドキュメントを再度開く
  7. テーブル内の属性の値を変更します
  8. 「名前を付けて保存」を使用して、新しい名前で保存します

OS X Yosemite では、これは常に次のバックトレースでクラッシュします。

_propertyAtIndexForEntityDescription ()
snapshot_get_value_as_object ()
-[NSManagedObject(_NSInternalMethods) _validatePropertiesWithError:] ()
-[NSManagedObject(_NSInternalMethods) _validateForSave:] ()
-[NSManagedObject validateForUpdate:] ()
-[NSManagedObjectContext(_NSInternalAdditions) _validateObjects:forOperation:error:exhaustive:forSave:] ()
-[NSManagedObjectContext(_NSInternalAdditions) _validateChangesForSave:] ()
-[NSManagedObjectContext(_NSInternalChangeProcessing) _prepareForPushChanges:] ()
-[NSManagedObjectContext save:] ()
-[NSPersistentDocument writeToURL:ofType:forSaveOperation:originalContentsURL:error:] ()
-[NSDocument _writeSafelyToURL:ofType:forSaveOperation:forceTemporaryDirectory:error:] ()
-[NSDocument _writeSafelyToURL:ofType:forSaveOperation:error:] ()
-[NSDocument writeSafelyToURL:ofType:forSaveOperation:error:] ()
-[NSPersistentDocument writeSafelyToURL:ofType:forSaveOperation:error:] ()
__66-[NSDocument saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke_22353 ()
__66-[NSDocument saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke2350 ()
__66-[NSDocument saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke_22222 ()
__110-[NSFileCoordinator(NSPrivate) _coordinateReadingItemAtURL:options:writingItemAtURL:options:error:byAccessor:]_block_invoke428 ()
-[NSFileCoordinator(NSPrivate) _invokeAccessor:orDont:andRelinquishAccessClaim:] ()
-[NSFileCoordinator(NSPrivate) _coordinateReadingItemAtURL:options:writingItemAtURL:options:error:byAccessor:] ()
-[NSDocument _fileCoordinator:coordinateReadingContentsAndWritingItemAtURL:byAccessor:] ()
-[NSDocument _fileCoordinator:asynchronouslyCoordinateReadingContentsAndWritingItemAtURL:byAccessor:] ()
__66-[NSDocument saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke2221 ()
-[NSDocument _prepareToSaveToURL:forSaveOperation:completionHandler:] ()
__66-[NSDocument saveToURL:ofType:forSaveOperation:completionHandler:]_block_invoke ()
-[NSDocument continueFileAccessUsingBlock:] ()
-[NSDocument _performFileAccessOnMainThread:usingBlock:] ()
-[NSDocument performAsynchronousFileAccessUsingBlock:] ()
-[NSDocument saveToURL:ofType:forSaveOperation:completionHandler:] ()
__85-[NSDocument saveToURL:ofType:forSaveOperation:delegate:didSaveSelector:contextInfo:]_block_invoke_2 ()
-[NSDocument _commitEditingThenContinue:] ()
__62-[NSPersistentDocument _documentEditor:didCommit:withContext:]_block_invoke ()

編集 1. 考えられる回避策:

「名前を付けて保存」操作中に元の管理オブジェクト コンテキストが保存されないようにすることで、クラッシュを回避することができます。「名前を付けて保存」した後、すぐに既存のドキュメントを閉じ、新しい場所からドキュメントを再度開きます。それはすべて非常に醜く、他の NSPersistentDocument の動作を壊す可能性があります。

編集 2. 上記の回避策では、保存されていない変更が失われます

元の管理オブジェクト コンテキストが保存されないようにすることで、クラッシュを回避できます。ただし、最終結果は、最後に保存された状態のドキュメントのコピーです。保存されていない変更は失われます。

編集 3. 内臓スナップショット

古い管理対象オブジェクト コンテキストが変更を新しいファイルに保存しようとするまでに、オブジェクト スナップショットはそのエンティティを認識しなくなります<_CDSnapshot_Entity_: 0x600001f3cfd0> (entity: (null); id: 0x40000b <x-coredata://83B64FD3-B5B9-44CB-976D-54C0326FDFF5/Entity/p1> ; data: (null))。インスタンス変数 backing が表示されません-[_CDSnapshot entity]。オブジェクトIDからそれを見つけるべきだと思います。

4

1 に答える 1

1

私のユースケースでうまくいくように見える回避策を思いつきました。

- (BOOL)writeToURL:(NSURL *)absoluteURL
            ofType:(NSString *)typeName
  forSaveOperation:(NSSaveOperationType)saveOperation
originalContentsURL:(NSURL *)absoluteOriginalContentsURL
             error:(NSError **)error
{
    if ((saveOperation == NSSaveAsOperation) && (absoluteOriginalContentsURL != nil)) {
        NSFileManager *fileManager = [NSFileManager defaultManager];

        if (![fileManager copyItemAtURL:absoluteOriginalContentsURL toURL:absoluteURL error:error]) {
            return NO;
        }

        NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
        NSPersistentStoreCoordinator *persistentStoreCoordinator = managedObjectContext.persistentStoreCoordinator;
        NSPersistentStore *store = [persistentStoreCoordinator persistentStoreForURL:absoluteOriginalContentsURL];

        [persistentStoreCoordinator setURL:absoluteURL forPersistentStore:store];

        if (![managedObjectContext save:error]) {
            return NO;
        }

        return YES;
    }

    return [super writeToURL:absoluteURL
                             ofType:typeName
                   forSaveOperation:saveOperation
                originalContentsURL:absoluteOriginalContentsURL
                              error:error];
}

名前を付けて保存するときに、古いドキュメントを新しい (一時的な) 場所にコピーします。次に、永続ストアに新しい URL を設定し、管理対象オブジェクト コンテキストが保留中の変更をその新しいドキュメントに保存できるようにします。

NSPersistentDocument は、 として渡された一時 URL の生成を処理しabsoluteURL、保存されたファイルを新しい場所に移動し、保存setFileURL:が完了すると呼び出します。

ドキュメントをサポートする SQLite ストアのジャーナリングを無効にしました。したがって、にある 1 つのファイルをコピーするだけで済みますabsoluteOriginalContentsURL

于 2016-04-27T12:27:02.303 に答える