多线程应用程序中NSManagedObjectContext的通用方法

chr*_*ris 13 iphone multithreading core-data objective-c asihttprequest

我在这里阅读了很多关于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];
}
Run Code Online (Sandbox Code Playgroud)

如果从主线程调用,保存操作很简单.从其他线程调用的保存操作需要在主线程内合并.为此,我有一个通用的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];
    }
}
Run Code Online (Sandbox Code Playgroud)

contextDidSave:函数中,我们执行合并,如果通过通知调用commit.

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

    [moc performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
                      withObject:saveNotification
                   waitUntilDone:YES];
}
Run Code Online (Sandbox Code Playgroud)

最后,我们使用以下方法清理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];
}
Run Code Online (Sandbox Code Playgroud)

这编译并且似乎有效,但我知道由于竞争条件,线程问题可能很棘手.有人看到这种方法有问题吗?

此外,我在异步请求(使用ASIHTTPRequest)的上下文中使用它,它从服务器获取一些数据并更新并在iPhone上插入商店.似乎NSThreadWillExitNotification在请求完成后不会触发,然后将相同的线程用于后续请求.这意味着相同的NSManagedObjectContext用于同一线程上的单独请求.这是一个问题吗?

chr*_*ris 8

发布此问题一年后,我终于构建了一个框架来概括和简化我使用Core Data的工作.它超越了原始问题,并添加了许多功能,使Core Data交互变得更加容易.详情请访问:https://github.com/chriscdn/RHManagedObject