【问题标题】:Core Data: NSManagedObjectContext, NSFetchResultsController, and UIManagedDocument核心数据:NSManagedObjectContext、NSFetchResultsController 和 UIManagedDocument
【发布时间】:2012-03-23 21:59:54
【问题描述】:

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

更新 MOC 的后台线程:

- (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 以使获取的结果控制器按预期运行有什么想法吗?

【问题讨论】:

  • 有人吗?完全卡住了..:/

标签: ios core-data concurrency nsfetchedresultscontroller uimanageddocument


【解决方案1】:

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

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

这里有一个链接指向面临类似问题的人,以及他们的“解决方案” - 有时最好的解决方案是知道没有一个:P

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      好的,我将编辑您的代码,让您了解它的外观。我假设您将 MOC 从 UIManagedDocument 传递到 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 确实非常有效且简单(一旦您了解规则)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-03
        • 1970-01-01
        • 2012-08-17
        • 2012-08-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多