使用CoreData的Grand Central Dispatch(GCD)

Mus*_*afa 22 iphone core-data grand-central-dispatch objective-c-blocks

我在我的申请中使用Grand Central Dispatch(GCD)来做一些繁重的工作.该应用程序使用Core-Data进行数据存储.这是我的场景(以及相关问题):

dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_queue_t request_queue = dispatch_queue_create("com.app.request", NULL);

dispatch_async(request_queue, ^{
    MyNSManagedObject *mObject = [self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];

    // … 
    // <heavy lifting>
    // … 

    // … 
    // <update mObject>
    // … 

    [self saveManagedObjectContext];
});     
Run Code Online (Sandbox Code Playgroud)

因此[self saveManagedObjectContext],fetchResultsController委托方法会自动调用.因此,UI更新逻辑启动.

现在的问题是,我需要用main_queue-saveManagedObjectContext?我应该在我执行的所有操作NSManagedObjectmain_queue?某些更新操作NSManagedObject可能需要2-3秒.请指教.

Mik*_*ler 60

核心数据有一个黄金法则 - 每个线程一个托管对象上下文.托管对象上下文不是线程安全的,因此如果您在后台任务中工作,您可以使用主线程来避免与UI操作的线程冲突,或者创建一个新的上下文来完成工作.如果工作要采取几秒钟后你应该做后者来阻止你的UI锁定.

为此,您需要创建一个新的上下文,并为其提供与主上下文相同的持久性存储:

NSManagedObjectContext *backgroundContext = [[[NSManagedObjectContext alloc] init] autorelease];
[backgroundContext setPersistentStoreCoordinator:[mainContext persistentStoreCoordinator]];
Run Code Online (Sandbox Code Playgroud)

执行您需要执行的任何操作,然后在保存新上下文时,您需要处理保存通知并将更改合并到主上下文中mergeChangesFromContextDidSaveNotification:.代码看起来应该是这样的:

/* Save notification handler for the background context */
- (void)backgroundContextDidSave:(NSNotification *)notification {
    /* Make sure we're on the main thread when updating the main context */
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(backgroundContextDidSave:)
                               withObject:notification
                            waitUntilDone:NO];
        return;
    }

    /* merge in the changes to the main context */
    [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}

/* ... */

/* Save the background context and handle the save notification */
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(backgroundContextDidSave:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:backgroundContext];

[backgroundContext save:NULL];

[[NSNotificationCenter defaultCenter] removeObserver:self
                                                name:NSManagedObjectContextDidSaveNotification
                                              object:syncContext];
Run Code Online (Sandbox Code Playgroud)

处理保存通知和合并很重要,否则您的主UI /上下文将看不到您所做的更改.通过合并,您的主fetchResultsController等将获得更改事件并更新您的UI,如您所料.

另一个需要注意的重要事项是NSManagedObject实例只能在从中获取的上下文中使用.如果您的操作需要对对象的引用,则必须将对象传递objectID给操作,并使用从新上下文重新获取NSManagedObject实例existingObjectWithID:.所以类似于:

/* This can only be used in operations on the main context */
MyNSManagedObject *objectInMainContext =
    [self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];

/* This can now be used in your background context */
MyNSManagedObject *objectInBackgroundContext =
    (MyNSManagedObject *) [backgroundContext existingObjectWithID:[objectInMainContext objectID]];
Run Code Online (Sandbox Code Playgroud)


Rob*_*und 17

您可能知道或已经注意到您必须在主线程上执行UI操作.正如您所提到的那样,当您保存UI更新时.您可以通过dispatch_sync在主线程上嵌套调用来解决此问题.

dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_queue_t request_queue = dispatch_queue_create("com.app.request", NULL);

__block __typeof__(self) blockSelf = self;

dispatch_async(request_queue, ^{
    MyNSManagedObject *mObject = [blockSelf.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];

    // update and heavy lifting...

    dispatch_sync(main_queue, ^{
      [blockSelf saveManagedObjectContext];
    });
});     
Run Code Online (Sandbox Code Playgroud)

使用 blockSelf 是为了避免产生意外的参考周期.(实用块)