13

ここで、NSManagedObjectContext とマルチスレッド アプリケーションに関する多くの投稿を読みました。また、CoreDataBooks の例を調べて、個別のスレッドが独自の NSManagedObjectContext を必要とする方法と、保存操作がメインの NSManagedObjectContext とマージされる方法を理解しました。この例は良いものですが、アプリケーション固有すぎることもわかりました。私はこれを一般化しようとしていますが、私のアプローチが正しいかどうか疑問に思っています。

私のアプローチは、現在のスレッドの NSManagedObjectContext を取得するための汎用関数を持つことです。この関数はメイン スレッドの NSManagedObjectContext を返しますが、別のスレッド内から呼び出された場合は新しいものを作成します (またはキャッシュからフェッチします)。それは次のようになります。

+(NSManagedObjectContext *)managedObjectContext {
    MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *moc = delegate.managedObjectContext;

    NSThread *thread = [NSThread currentThread];

    if ([thread isMainThread]) {
        return moc;
    }

    // a key to cache the context for the given thread
    NSString *threadKey = [NSString stringWithFormat:@"%p", thread];

    // delegate.managedObjectContexts is a mutable dictionary in the app delegate
    NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;

    if ( [managedObjectContexts objectForKey:threadKey] == nil ) {
        // create a context for this thread
        NSManagedObjectContext *threadContext = [[[NSManagedObjectContext alloc] init] autorelease];
        [threadContext setPersistentStoreCoordinator:[moc persistentStoreCoordinator]];
        // cache the context for this thread
        [managedObjectContexts setObject:threadContext forKey:threadKey];
    }

    return [managedObjectContexts objectForKey:threadKey];
}

メインスレッドから呼び出された場合、保存操作は簡単です。他のスレッドから呼び出された保存操作は、メイン スレッド内でマージする必要があります。そのために、私は一般的なcommit機能を持っています:

+(void)commit {
    // get the moc for this thread
    NSManagedObjectContext *moc = [self managedObjectContext];

    NSThread *thread = [NSThread currentThread];

    if ([thread isMainThread] == NO) {
        // only observe notifications other than the main thread
        [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(contextDidSave:)
                                                 name:NSManagedObjectContextDidSaveNotification
                                               object:moc];
    }

    NSError *error;
    if (![moc save:&error]) {
        // fail
    }

    if ([thread isMainThread] == NO) {
        [[NSNotificationCenter defaultCenter] removeObserver:self 
                                                    name:NSManagedObjectContextDidSaveNotification 
                                                  object:moc];
    }
}

の通知によって呼び出された場合、contextDidSave:関数でマージを実行しcommitます。

+(void)contextDidSave:(NSNotification*)saveNotification {
    MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
    NSManagedObjectContext *moc = delegate.managedObjectContext;

    [moc performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
                      withObject:saveNotification
                   waitUntilDone:YES];
}

最後に、これを使用して NSManagedObjectContext のキャッシュをクリーンアップします。

+(void)initialize {
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(threadExit) 
                                                 name:NSThreadWillExitNotification 
                                               object:nil]; 
}

+(void)threadExit {
    MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
    NSString *threadKey = [NSString stringWithFormat:@"%p", [NSThread currentThread]];  
    NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;    

    [managedObjectContexts removeObjectForKey:threadKey];
}

これはコンパイルされ、機能しているように見えますが、競合状態のためにスレッドの問題が厄介になる可能性があることはわかっています。このアプローチに問題がある人はいますか?

また、サーバーから一部のデータをフェッチし、iPhone のストアを更新して挿入する非同期要求 (ASIHTTPRequest を使用) のコンテキスト内からこれを使用しています。リクエストの完了後に NSThreadWillExitNotification が起動されないようで、その後のリクエストには同じスレッドが使用されます。これは、同じ NSManagedObjectContext が同じスレッドの個別の要求に使用されることを意味します。これは問題ですか?

4

2 に答える 2

8

この質問を投稿してから 1 年後、私は最終的に Core Data の作業を一般化して簡素化するフレームワークを構築しました。元の質問を超えて、多くの機能を追加して Core Data の操作をより簡単にします。詳細はこちら: https://github.com/chriscdn/RHManagedObject

于 2011-12-02T13:51:15.987 に答える
0

最終的に問題をよりよく理解した後、解決策を見つけました。私の解決策は、上記の質問に直接対処するものではありませんが、そもそもなぜスレッドを処理しなければならなかったのかという問題に対処しています。

私のアプリケーションでは、非同期リクエストに ASIHTTPRequest ライブラリを使用しています。サーバーからいくつかのデータをフェッチし、デリゲートrequestFinished関数を使用してコア データ オブジェクトを追加/変更/削除します。関数は別のrequestFinishedスレッドで実行されていましたが、これは非同期リクエストの自然な副作用だと思いました。

より深く掘り下げた後、ASIHTTPRequest は意図的に別のスレッドで要求を実行することがわかりましたが、ASIHTTPRequest のサブクラスでオーバーライドすることができます。

+(NSThread *)threadForRequest:(ASIHTTPRequest *)request {
    return [NSThread mainThread];
}

この小さな変更によりrequestFinished、メイン スレッドが追加され、アプリケーション内のスレッドを気にする必要がなくなりました。

于 2010-08-16T09:57:12.683 に答える