在后台获取Core Data对象:对象没有出现故障

时间:2011-12-09 15:13:56

标签: ios core-data nsmanagedobject grand-central-dispatch nsmanagedobjectcontext

我需要一些帮助来使用Core Data with GCD中的对象;我似乎得到NSManagedObjects,即使我访问他们的属性,也没有出现在主线程中。希望得到一些帮助。

这就是我正在做的事情:在启动时,我需要从Core Data DB加载一个人员列表,在后台进行一些自定义处理,然后重新加载表以显示名称。我通过仅将objectID传递到GCD队列来遵循Core Data多线程的指导原则。但是当我在主线程上重新加载tableview时,我从未看到为联系人显示的名称(或其他属性),并且经过仔细检查,NSManagedObjects在主线程上变成了错误,即使我访问了各种属性的cellForRowAtIndexPath。当我NSLog时,name属性在后台线程中可见;它也在cellForRowAtIndexPath中的NSLogs主线程上正确显示。但无论我做什么,它们都不会在tableView中显示。我尝试使用点表示法访问name属性,以及valueForKey,但都没有工作。

这是我的代码......它是从FRC初始化程序调用的:

- (NSFetchedResultsController *)fetchedResultsController 
{
    if (__fetchedResultsController != nil) 
    {
        return __fetchedResultsController;
    }

    __fetchedResultsController = [self newFetchedResultsControllerWithSearch:nil]; // creates a new FRC

    [self filterAllContactsIntoDictionary: __fetchedResultsController];
    return [[__fetchedResultsController retain] autorelease];
}  


- (void) filterAllContactsIntoDictionary: (NSFetchedResultsController *) frc
{

    NSArray *fetchedIDs = [[frc fetchedObjects] valueForKey:@"objectID"];
    NSArray *fetched = [frc fetchedObjects];

    if (filterMainQueue == nil) {
        filterMainQueue = dispatch_queue_create("com.queue.FilterMainQueue", NULL);
    }
    dispatch_async(self.filterMainQueue, ^{

        NSManagedObjectContext *backgroundContext = [[[NSManagedObjectContext alloc] init] autorelease];
        [backgroundContext setPersistentStoreCoordinator:[[self.fetchedResultsController managedObjectContext] persistentStoreCoordinator]];
        NSMutableArray *backgroundObjects = [[NSMutableArray alloc] initWithCapacity: fetchedIDs.count];

        // load the NSManagedObjects in this background context
        for (NSManagedObjectID *personID in fetchedIDs)
        {
            Person *personInContext = (Person *) [backgroundContext objectWithID: personID];
            [backgroundObjects addObject:personInContext];
        }
        [self internal_filterFetchedContacts: backgroundObjects]; // loads contacts into custom buckets

        // done loading contacts into character buckets ... reload tableview on main thread before moving on
        dispatch_async(dispatch_get_main_queue(), ^{

            CGPoint savedOffset = [self.tableView contentOffset];
            [self.tableView reloadData];
            [self.tableView setContentOffset:savedOffset];

        });
    });
}

我在这里做错了什么?有没有其他方法可以明确地让Person对象在主线程上触发它们的错误?或者我是否对GCD队列和核心数据做错了,我不知道? 感谢。

3 个答案:

答案 0 :(得分:8)

为什么不采取简单的路线,因为你没有保存任何新东西? 不是为后台线程创建额外的上下文并使用ID,而是在锁定后使用后台线程中的主managedObjectContext

例如:

- (void) filterAllContactsIntoDictionary: (NSFetchedResultsController *) frc
{

    if (filterMainQueue == nil) {
        filterMainQueue = dispatch_queue_create("com.queue.FilterMainQueue", NULL);
    }
    dispatch_async(self.filterMainQueue, ^{

        NSManagedObjectContext *context = ... // get the main context.
        [context lock];     // lock the context.

        // do something with the context as if it were on the main thread.

        [context unlock];   // unlock the context.

        dispatch_async(dispatch_get_main_queue(), ^{
           CGPoint savedOffset = [self.tableView contentOffset];
           [self.tableView reloadData];
           [self.tableView setContentOffset:savedOffset];
       });
   });
}

当我使用performSelectorInBackground调用方法时,这对我有用,所以我猜它也适用于GCD调度。

答案 1 :(得分:0)

好吧,mergeChangesFromContextDidSaveNotification:是你的朋友。你需要在主线程上告诉MOC其他地方有变化。这样就可以了。

Here's Apple's documentation。引用那里:

  

此方法刷新在其他上下文中已更新的任何对象,任何新插入的对象中的错误,并在已删除的对象上调用deleteObject ::。

答案 2 :(得分:0)

编辑:原始答案已删除,OP未在后台获取

我仔细查看了您的代码,看起来您没有做任何会改变数据和/或影响主线程上下文的内容。

  1. 主线程上有fetchedResultsController。据推测,这是有效的,你的表填充了数据。这是真的吗?

  2. 调用filterAllContentsIntoDictionary时,将fetchedResultsController当前objectID的数组传递给后台线程并对它们进行一些处理(可能根据某些条件对它们进行过滤)但您没有更改数据并保存backgroundContext

  3. internalFilterFetchedContents是一个黑盒子。在不知道你打算做什么的情况下,很难说为什么它不起作用。

  4. 完成此操作后,您将在主线程上重新加载表。

  5. 您尚未对商店,上下文或fetchedResultsController进行任何更改,因此该表显示了之前执行的相同数据。缺少的细节将进一步帮助:

    1. 您的tableView是否显示fetchedResultsController开头的正确数据?如果没有,很可能你唯一的问题是处理tableView委托和dataSource方法,其余部分并不真正相关。

    2. 您打算在filterAllContentsIntoDictionaryinternalFilterFetchedContents中做些什么?

    3. 如果您的目的是过滤fetchedResultsController显示的数据,不确定您是否需要在后台执行任何操作。如果您修改fetchRequest并再次执行performFetch,您的表格将根据新结果重新加载。

    4. 我需要更多帮助,请回答我的问题,在帖子中添加更多相关代码,如果我错过任何问题以及您想要完成的任务,请告诉我。

      祝你好运!