2017-01-10 8 views
1

iOS 10の新しいコアデータスタックをテストしています。テストアプリケーションはJSONデータをコアデータに解析しています。ユーザーがUIにアクセスしている間に発生します。iOS 10のコアデータ保存エラー:未解決のエラーエラードメイン= NSCocoaErrorDomainコード= 133020

私はデフォルトのコアデータスタックを使用しており、バックグラウンドコンテキストを使用しています。 AppDelegate.mで

- (NSPersistentContainer *)persistentContainer { 
    // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it. 
    @synchronized (self) { 
     if (_persistentContainer == nil) { 
      _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"CoreDataTestingMDC"]; 
      [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) { 
       if (error != nil) { 
        NSLog(@"Unresolved error %@, %@", error, error.userInfo); 
        abort(); 
       } 
      }]; 
     } 
    } 
    _persistentContainer.viewContext.automaticallyMergesChangesFromParent = YES; 
    return _persistentContainer; 
} 

Iは、詳細ビューのコアデータマスター・ビュー・コントローラのエンティティと詳細な属性を示す単純な主従UIを持っています。ユーザーがマスタービューをスクロールしないと、すべて正常に動作します。ユーザーがスクロールすると、通常は保存時にこのエラーが発生します。

Unresolved error Error Domain=NSCocoaErrorDomain Code=133020 "(null)" UserInfo={conflictList=(
"NSMergeConflict (0x600000667c00) for NSManagedObject (0x610000096490) with objectID '0xd000000000440000 <x-coredata://CFF27A51-8F9E-4898-A4EA-CD85C0AFF300/ContentItem/p17>' 
with oldVersion = 44 and newVersion = 45... 

これはまったく同じプロパティを持つ競合する項目を一覧表示します。これは、保存操作のためにAppDelegateに戻され

- (NSManagedObjectContext *)createBackgroundContext { 
    return [self.persistentContainer newBackgroundContext]; 
} 

- (void)saveContext:(NSManagedObjectContext *) theContext { 
    NSError *error = nil; 
    if ([theContext hasChanges] && ![theContext save:&error]) { 
     NSLog(@"Unresolved error %@, %@", error, error.userInfo); 
     abort(); 
    } 
} 

UIがある私AppDelegateでも

は、私は、バックグラウンド・コンテキストを生成するための簡単な便利なメソッドを追加しました期待どおりにviewContextで実行しています。私はすべてのJSONパーサ記述のバックグラウンドコンテキストを使用するのに非常に注意しています。なぜこれがクラッシュするのか分かりません。

更新:

アプリが最初の実行後に実行されるたびに、エラーが発生したことが表示されます。クリーンなシミュレータで、またはアプリケーションを削除した後で、それを正常にテストできます。データをコアデータに解析し、ユーザーがアプリケーションとやり取りしている間も更新されます。 2回目のビルドと実行では、上記のエラーでアプリケーションがクラッシュします。

答えて

1

複数の書き込みが同時に発生しているように見えます。これを解決するには、コアデータを単一の同期方法で書き込む必要があります。あなたのコア・データ・マネージャで

NSOperationQueue

_persistentContainerQueue = [[NSOperationQueue alloc] init]; 
_persistentContainerQueue.maxConcurrentOperationCount = 1; 

を作成し、すべてこのキュー使って書いてください:あなたはブロックがエンキューされ​​を呼び出すと何のマージが存在しないことを保証するために

- (void)enqueueCoreDataBlock:(void (^)(NSManagedObjectContext* context))block{ 
    void (^blockCopy)(NSManagedObjectContext*) = [block copy]; 

    [self.persistentContainerQueue addOperation:[NSBlockOperation blockOperationWithBlock:^{ 
     NSManagedObjectContext* context = self.persistentContainer.newBackgroundContext; 
     [context performBlockAndWait:^{ 
      blockCopy(context); 
      [context save:NULL]; //Don't just pass NULL here. look at the error and log it to your analytics service 
     }]; 
    }]]; 
} 

を紛争。しかし、この設定を無効にするviewContextに書き込むとします。同様に、作成する他のコンテキスト(newBackgroundContextまたはperformBackgroundTask)も、書き込みキューの外側にあるため、読み取り専用として扱う必要があります。

最初に私はNSPersistentContainerperformBackgroundTaskに内部キューがあり、初期テストがそれをサポートしていると思いました。より多くのテストの後、私はそれがマージ競合につながる可能性があることもわかりました。

+0

私がnewBackgroundContextを使用しない場合は、performBackgroundTaskでviewContextを使用することをお勧めしますか?私は数十から数百のアイテムにどこかを解析するかもしれません。 UIをロックアップしたくありません... – BBruce

+0

'performBackgroundTask'は、使用するバックグラウンドコンテキストを提供します。あなたは** viewContextを使って書くことは絶対にありません。 –

+0

ありがとうございました。 'performBackgroundTask'を完全に使うためにパーサーを書き直しました。今とてもうまくいっています。 :) – BBruce

関連する問題