0

我在使用后台线程更新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 以使获取的结果控制器按预期运行的任何想法?

4

3 回答 3

0

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

于 2012-04-03T14:31:55.223 回答
0

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

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

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

Core Data 托管对象在重启模拟器之前看不到相关对象

于 2012-04-02T09:54:27.553 回答
0

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

于 2012-04-17T22:23:15.630 回答