マルチスレッドコアデータ

ご存じのとおり、Core Dataは強力なAppleオブジェクトグラフ管理フレームワークです。 コアデータに関するHabréの記事は多数ありますが、マルチスレッドのカバーが不十分であり、ほとんどの人がそれを正しく実装する方法の質問をしたようです。



一般規定



要するに、コアデータスタックはいくつかの主要部分で構成されています。







1)NSPersistentStore。バイナリファイル、XML、SQLiteファイルを使用できます。

2)NSManagedObjectModel。これは、データモデルのコンパイル済みバイナリバージョンです。

3)NSPersistentStoreCoordinatorは、NSPersistentStoreおよびNSManagedObjectModelからのデータのロード、保存およびキャッシュに従事しています。

4)NSManagedObjectContext、NSPersistentStoreからメモリへのデータのロード、インスタンスの操作。

5)NSManagedObject-データモデルのオブジェクト。



私の意見では、この奇跡の主な不快な特徴は、NSManagedObjectContextがスレッドセーフではないということです。



スタックの初期化



データベースサイズが大きいと、移行中にメインスレッドのスタックの初期化に30秒以上かかることがあります。 これにより、システムは単にアプリケーションを強制終了します。 解決方法があります。別のスレッドでスタックを初期化します。



dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //  NSManagedObjectModel NSURL *modelURL = [[NSBundle mainBundle] URLForResource:kModelFileName withExtension:@"momd"]; NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; //  NSPersistentStoreCoordinator NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom]; //   NSPersistentStoreCoordinator ,          NSURL *doURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; NSURL *storeURL = [doURL URLByAppendingPathComponent:@"CoreData.sqlite"]; NSError *error = nil; NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]; //   });
      
      







そのため、アプリケーションが起動され、ユーザーはUIラグを受け取らず、誰もが満足しています。 さらに続きます。



メインコンテキストの作成



上で書いたように、NSManagedObjectContextはスレッドセーフではありません。 したがって、メインスレッドでメインアプリケーションコンテキストを保持することが適切です。 しかし、この場合、このコンテキストを維持しながらUIが遅くなります。どうすればよいですか? しかし、ここでは、iOS 6でNSManagedObjectContext型が登場しました。



1)NSMainQueueConcurrencyType-メインスレッドからのみ利用可能。

2)NSPrivateQueueConcurrencyType-バックグラウンドスレッドで実行されます。

3)NSConfinementConcurrencyType-作成されたスレッドで実行されます。



また、子コンテキストを作成する機会。 保存するとき、子コンテキストはすべての変更を親にプッシュします。 したがって、次のように、CoreDataを操作するためのマネージャーを装備する機会があります。



 //           . // _daddyManagedObjectContext        ,  . _daddyManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; [_daddyManagedObjectContext setPersistentStoreCoordinator:psc]; //      main-thread context,     dispatch_async(dispatch_get_main_queue(), ^{ _defaultManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; //     ,       [_defaultManagedObjectContext setParentContext:_daddyManagedObjectContext]; });
      
      







この時点で、コアデータを操作するためのマネージャーの初期化が終了し、メインスレッドの娘だけでなく、pr索好きな目から隠された父親のコンテキストができました。



子コンテキストの作成



簡単に推測できるように、バックグラウンドフローで作業する場合、上記で作成した子コンテキストを祖先として追加するだけで、コンテキストを作成できます。



 - (NSManagedObjectContext *)getContextForBGTask { NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; [context setParentContext:_defaultManagedObjectContext]; return context; }
      
      







このコンテキストを保存すると、その親への変更が常に保存されます。 したがって、_defaultManagedObjectContext(実際の親に変更をプッシュする)には常に最も関連性の高い情報があります。



コンテキストの保存



残された最も重要なことは保存です。 バックグラウンドストリームに存在するコンテキストは、performBlock:およびperformBlockAndWait:を介してのみアクセスできます。 したがって、バックグラウンドストリームの保存は次のようになります。



 - (void)saveContextForBGTask:(NSManagedObjectContext *)bgTaskContext { if (bgTaskContext.hasChanges) { [bgTaskContext performBlockAndWait:^{ NSError *error = nil; [backgroundTaskContext save:&error]; }]; // Save default context } }
      
      







子コンテキストを保存したら、親を保存する必要があります。



 - (void)saveDefaultContext:(BOOL)wait { if (_defaultManagedObjectContext.hasChanges) { [_defaultManagedObjectContext performBlockAndWait:^{ NSError *error = nil; [_defaultManagedObjectContext save:&error]; }]; } //    _defaultManagedObjectContext    ,   _daddyManagedObjectContext void (^saveDaddyContext) (void) = ^{ NSError *error = nil; [_daddyManagedObjectContext save:&error]; }; if ([_daddyManagedObjectContext hasChanges]) { if (wait) [_daddyManagedObjectContext performBlockAndWait:saveDaddyContext]; else [_daddyManagedObjectContext performBlock:saveDaddyContext]; } }
      
      







おわりに



数年の間、私は開発者からCore Dataに多くのマイナスがあるとよく耳にします。そのため、たとえばFMDBを優先して選択します。 この記事の目的は、まさにこの神話を払拭することです。

Core Dataを操作するために多くのフレームワークが作成されていますが、メインのフレームワークはMagicalRecordです。 膨大な機能が含まれています。 内部で上記の方法に従ってほぼ動作することは注目に値します。 フレームワークは適切に適用する必要があります。つまり、その仕組みを理解する必要があります。



以上です。 ご清聴ありがとうございました。



All Articles