0

我遇到一些麻烦,使用后台线程在UIManagedDocument/Core Data更新数据。具体来说,我使用NSFetchResultsController更新基于来自后台线程的地理编码数据的地图注释(在合并回我的主要MOC之后),但由于UIManagedDocument向数据库和/或其数据提交数据的方式多个MOC(父母和小孩)。如果我关闭应用程序并重新打开,则注释被填充,因此对持久存储的提交在某个时间点发生,但是我不清楚如何强制执行这样的提交,从而更新NSFetchResultsController。下面是一些代码:核心数据:NSManagedObjectContext中,NSFetchResultsController和UIManagedDocument

后台线程更新商务部:

- (void) populateGPSCoordsInClubsInContext: (NSManagedObjectContext *) mainCtx 
{    
    dispatch_queue_t MapFetchQ = dispatch_queue_create("Google Map Data Fetcher", NULL); 
    dispatch_async(MapFetchQ, ^{ 

     NSManagedObjectContext * ctxThread = [[NSManagedObjectContext alloc] init]; 
     [ctxThread setPersistentStoreCoordinator:mainCtx.persistentStoreCoordinator]; 


     NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Club"]; 
     request.predicate = [NSPredicate predicateWithFormat:@"inRegion.name=%@", self.name]; 
     NSError *error = nil; 

     NSArray * clubs = [ctxThread executeFetchRequest:request error:&error]; 

     NSLog(@"[%@] Fetching map data. Club count is %d", self.name, [clubs count]); 

     int delayCounter = 0; 

     for(Club * club in clubs) 
     { 
      if(![club.hasCoord boolValue] && club != nil) 
      { 
       delayCounter++; // to deal with google maps api's DoS protection    

       [club setLongitudeAndLattitudeFromGoogle]; 
       NSError * error; 

       if(![ctx save:&error]) 
       NSLog(@"[%@] Problem saving region to database.", self.name); 

      } 

      if(delayCounter == 8) 
      {    
       [NSThread sleepForTimeInterval:(NSTimeInterval)2.0]; 
       delayCounter = 0; 
      } 
     } 
    }); 
    dispatch_release(MapFetchQ); 
} 

当这些节省被称为,我抢在主线程的通知(在我的应用程序委托),像这样:

- (void) contextDidSave: (NSNotification *) notification 
{ 
    NSManagedObjectContext * ctx = [self.clubsDB managedObjectContext]; 
    [ctx mergeChangesFromContextDidSaveNotification:notification]; 

    NSArray * updates = [[notification.object updatedObjects] allObjects]; 

    for(Club * club in updates) // This never fires because updates never has objects 
    { 
     NSLog(@"*********** %@", club.name); 
    } 

    NSLog(@"[%@] %@", [self class], NSStringFromSelector(_cmd)); 

} 

而且我已经把我的获取结果控制器像这样(谓语是正确的,结果与应用程序重启数据已提交到存储后预计):

-(void) setupFRC 
{ 

    NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Club"]; 
    request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]; 
    request.predicate = [NSPredicate predicateWithFormat:@"inRegion.name=%@ AND hasCoord=%@",[self.clubsDB regionTitleAsString], [NSNumber numberWithBool:YES]]; // Follow the relationshop and only display clubs from THIS region. 

    //request.predicate = [NSPredicate predicateWithFormat:@"inRegion.name=%@",[self.clubsDB regionTitleAsString]]; 

    self.debug = YES; 

    self.fetchedResultsController = 
    [[NSFetchedResultsController alloc] initWithFetchRequest:request 
             managedObjectContext:self.clubsDB.managedObjectContext 
              sectionNameKeyPath:nil 
                cacheName:nil]; 
} 

有关如何更新相应的MOC以使获取的结果控制器按所需行为的任​​何想法?

+0

任何人都可以吗?完全卡住..:/ – Andrew 2012-03-26 20:00:44

回答

0

我有UIManagedDocument发送它的提交方式的问题。我能想到的唯一解决方案是停止使用UIManagedDocument,并使用默认Master-Detail模板中提供的PersistentStore中的上下文。

编辑:经过进一步研究,似乎没有办法让UIManagedDocument提交更改,所以最好传递一个从持久存储库创建的上下文。似乎并非巧合,Apple尚未为UIManagedDocument提供任何可用的示例代码。我会坚持使用默认模板。

这里的人面临着类似的问题的链接,而他们的“解决方案” - 有时是最好的解决办法是要知道,没有一个:P

Core Data managed object does not see related objects until restart Simulator

0

实际上,我是能够解决的问题。诀窍是确保上下文在设置FRC之前预先填入我正在编辑的对象。坦率地说,这是非常深奥的事实,UIManagedDocument不能按预期工作(或者甚至文档解释它),令人不安。

0

好吧,我要编辑你的代码,让你知道它应该是什么样子。我假设你从UIManagedDocument传递MOC到populateGPSCoordsInClubsContext。请注意,有很小的差别是在你已经做什么,但我们知道,一行代码可以使所有的差异...

// NOTE: Make it clear you expect to work on a document... 
- (void) populateGPSCoordsInClubsInContext: (UIManagedDocument *) document 
{    
    dispatch_queue_t MapFetchQ = dispatch_queue_create("Google Map Data Fetcher", NULL); 
    dispatch_async(MapFetchQ, ^{ 

     NSManagedObjectContext * ctxThread = [[NSManagedObjectContext alloc] init]; 
     // NOTE: Make changes up into the context of the document 
     ctxThread.parentContext = document.managedObjectContext;  

     NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Club"]; 
     request.predicate = [NSPredicate predicateWithFormat:@"inRegion.name=%@", self.name]; 
     NSError *error = nil; 

     NSArray * clubs = [ctxThread executeFetchRequest:request error:&error]; 

     NSLog(@"[%@] Fetching map data. Club count is %d", self.name, [clubs count]); 

     int delayCounter = 0; 

     for(Club * club in clubs) 
     { 
      if(![club.hasCoord boolValue] && club != nil) 
      { 
       delayCounter++; // to deal with google maps api's DoS protection    

       [club setLongitudeAndLattitudeFromGoogle]; 
       NSError * error; 

       // NOTE: This notifies the parent context of the changes. 
       if(![ctx save:&error]) 
       NSLog(@"[%@] Problem saving region to database.", self.name); 
       // NOTE: However, since a UIManagedDocument is an "auto-save" 
       // document, we need to tell it that is is dirty... 
       [document updateChangeCount:UIDocumentChangeDone]; 
      } 

      if(delayCounter == 8) 
      {    
       [NSThread sleepForTimeInterval:(NSTimeInterval)2.0]; 
       delayCounter = 0; 
      } 
     } 
    }); 
    dispatch_release(MapFetchQ); 
} 

一个关于做这种方式很酷的事情,是你甚至不必处理通知(至少不是为了保持一致)。

如果你想做它的其他方式,你可以做到这一点?

 ctxThread.parentContext = document.parentContext;  

然后,您不会在文档上调用updateChangeCount。这些更改将进入父上下文,并进入文件。但是,这样做,您不必处理通知,因为将来的提取会自动看到它们。当然,如果你想刷新更改,你仍然可以处理通知,但这一切。要在提取时查看它们,您不必执行其他任何操作。

与流行的观点相反,UIManagedDocument真的非常有效和简单(一旦你知道规则)。

相关问题