在后台提取核心数据对象:对象无故障

问题描述:

我需要一些帮助才能使用Core Data和GCD中的对象;即使在我访问它们的属性时,我似乎也会获得没有故障的NSManagedObjects进入主线程。希望得到一些帮助。在后台提取核心数据对象:对象无故障

这就是我在做的事情:在启动时,我需要从核心数据数据库中加载人员列表,在后台执行一些自定义处理,然后重新加载表以显示名称。我遵循Core Data多线程指南,只通过将objectID传入GCD队列。但是,当我在主线程上重新加载tableview时,我从来没有看到为联系人显示的名称(或其他属性),并且仔细检查后,即使我访问了主线程中的各种属性,NSManagedObject仍然是主线程上的错误的cellForRowAtIndexPath。当我NSLog时,name属性在后台线程中可见;并且它也正确显示在cellForRowAtIndexPath中的NSLog中的主线程上。但是,无论我做什么,它们都不会显示在tableView中。我尝试访问名称属性使用点符号,以及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队列和核心数据方面出现问题,而我并不知道? 谢谢。

+0

我很乐意帮助你。准确地知道“[self internal_filterFetchedContacts:backgroundObjects]”的作用会有帮助。 而且,我想知道你的CellForRowAtIndexPath从哪里获取数据。 –

为什么不走容易的路线,因为你没有保存任何新东西? 除了为后台线程创建额外的上下文并使用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调度工作了。

+0

锁定上下文是否会阻塞主线程(这样UI将被冻结)? –

+0

不,它不。在上下文被锁定的情况下,UI工作正常。 – iska

+0

谢谢,现在看起来工作正常,所以我会将其标记为正确的答案。我唯一担心的是将NSManagedObjects传递给另一个队列进行后台处理,但是如果我可以在另一个队列上使用相同的上下文和NSManagedObjects(在锁定上下文之后),它应该可以工作。将对其进行彻底测试以确保它很健壮,但似乎现在正在工作。 –

那么,mergeChangesFromContextDidSaveNotification:是你的朋友。您需要在主线程上告诉MOC其他地方有变化。这将做到这一点。

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

这种方法将刷新任何新插入的对象已在其他情况下被更新的对象,故障,并调用DeleteObject的::对那些已被删除。

+0

但在后台线程中没有任何内容。它只是加载一些瞬态属性。所以没有NSNotification对象传入mergeChangesFromContextDidSaveNotification。 –

编辑:原来的答案删除,OP是不是在后台获取

我看着你的代码更近,它看起来并不像你正在做的事情,这将改变数据和/或影响的情况下在主线程上。

  1. 您在主线程上有fetchedResultsController。据推测,这是工作和你的表填充数据。这是真的?

  2. 当调用filterAllContentsIntoDictionary,你通过fetchedResultsController的当前的ObjectID的阵列以在后台线程并执行对它们进行一些处理(大概过滤他们基于一些标准),但没有更改的数据和保存backgroundContext

  3. internalFilterFetchedContents是一个黑匣子。不知道你打算做什么,很难说为什么它不起作用。

  4. 完成后,重新加载主线程上的表。

所以当然,该表显示之前也做了同样的数据您尚未进行到店里,上下文,或fetchedResultsController任何更改。丢失的细节,进一步帮助有:

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

  2. 你打算在filterAllContentsIntoDictionaryinternalFilterFetchedContents中发生什么?

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

我需要更多的帮助,请回答我的问题,更相关的代码添加到您的文章,让我知道如果我错过什么WRT的问题,你要完成的任务。

祝你好运!

+0

谢谢,但如果你看看代码,我不会在后台获取......我后处理在主线程中获取的对象。 –

+0

好的,我会仔细看看代码。你的问题的标题是指在后台抓取所以可能应该改变这一点。 – XJones

+0

internalFilterFetchedContents只是通过名称,并将它们放入显示联系人(A,B,C等)的正确桶中,并对排序进行一些自定义。如果我不在后台执行后处理,并且在主线程上执行该操作(但可能需要4-5秒才能加载长列表),那么tableview可以正常工作。在后台执行internalFilterFetchedContents时,它会显示所有行并将其正确排序,只是它们没有显示名称或任何其他属性。 –