NSManagedObjectContext - 当父母改变时如何更新孩子?

Posted

技术标签:

【中文标题】NSManagedObjectContext - 当父母改变时如何更新孩子?【英文标题】:NSManagedObjectContext - how to update child when parent changes? 【发布时间】:2016-03-31 15:51:44 【问题描述】:

Apple 文档不清楚(或者我找不到)当 parentMOC 插入后保存时在父子 MOC 的情况下会发生什么。

我正在使用 MARCUS ZARRA 的 http://martiancraft.com/blog/2015/03/core-data-stack/ 方法,顶部有一个 privateQMOC,而 childMainMOC 作为主线程之一。

问题

我通过在 privateMOC 上调用 save 的后台 Internet 请求将 10,000 个对象添加到 privateMOC,但任何基于 childMainMOC 上下文构建的 NSFetchedResultsController 在父级保存后都不会调用我的委托。因此界面不会更新以显示 parentMOC 中的更改。

我想调用一些东西来更新 childMainMOC 中的所有对象 - 然后它应该调用子控制器上的委托方法。

或其他一些解决方案。

【问题讨论】:

【参考方案1】:

好的 - 所以我想通了。

模型如下: 马库斯·扎拉的http://martiancraft.com/blog/2015/03/core-data-stack/ privateMOC 是根,这允许更好的性能,并且出于其他原因也需要这种方式。我只使用了 2 个 MOC:一个私有根和一个子线程,它是一个主线程。

然后阅读: 基本上,他解释了 Core Data 如何处理通知等。 http://benedictcohen.co.uk/blog/archives/308

那么 - 我需要做的最后一件事是:确保程序中的所有 objectID 都是真正永久的。

- (id)insertNewObjectForEntityName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context 

NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context];

// Make sure all inserted objects have a permanent ID.
// THIS IS VITAL. Without getting the permanentIDs, changes that come in from the web will not propogate to the child mainQ MOC and the UI will not update.
// Tested to be performant.
//NSLog(@"testing this - object.objectID is a temp now I think: %@ isTemp:%d", object.objectID, (int) [object.objectID isTemporaryID]);
// http://***.com/questions/11990279/core-data-do-child-contexts-ever-get-permanent-objectids-for-newly-inserted-obj
NSError* error = nil;
[context obtainPermanentIDsForObjects:@[object] error:&error];
if (error || [object.objectID isTemporaryID])
    NSLog(@"obtainPermanentIDsForObjects is NOT WORkING - FIX: a new %@ isTemp: %d !!", entityName, (int) [object.objectID isTemporaryID]);

return object;

我还需要按照 Benedicts 的文章 - 监听父根私有 MOC 中的更改

/// . http://benedictcohen.co.uk/blog/archives/308  good info !
/// I need this firing as sometimes objects change and the save notification below is not enough to make sure the UI updates.
- (void)privateQueueObjectContextDidChangeNotification:(NSNotification *)notification 
    NSManagedObjectContext *changedContext = notification.object;
    NSManagedObjectContext *childContext = self.mainQueueObjectContext;
    BOOL isParentContext = childContext.parentContext == changedContext;
    if (!isParentContext) return;

    //Collect the objectIDs of the objects that changed
    __block NSMutableSet *objectIDs = [NSMutableSet set];
    [changedContext performBlockAndWait:^
        NSDictionary *userInfo = notification.userInfo;
        for (NSManagedObject *changedObject in userInfo[NSUpdatedObjectsKey]) 
            [objectIDs addObject:changedObject.objectID];
        
        for (NSManagedObject *changedObject in userInfo[NSInsertedObjectsKey]) 
            [objectIDs addObject:changedObject.objectID];
        
        for (NSManagedObject *changedObject in userInfo[NSDeletedObjectsKey]) 
            [objectIDs addObject:changedObject.objectID];
              
    ];

    //Refresh the changed objects
    [childContext performBlock:^
        for (NSManagedObjectID *objectID in objectIDs) 
            NSManagedObject *object = [childContext existingObjectWithID:objectID error:nil];
            if (object) 
                [childContext refreshObject:object mergeChanges:YES];
                //NSLog(@"refreshing %@", [object description]);
            
        
    ];

   - (void)privateQueueObjectContextDidSaveNotification:(NSNotification *)notification 
    //NSLog(@"private Q MOC has saved");
    [self.mainQueueObjectContext performBlock:^
        [self.mainQueueObjectContext mergeChangesFromContextDidSaveNotification:notification];
        // I had UI update problems which I fixed with mergeChangesFromContextDidSaveNotification along with obtainPermanentIDsForObjects: in the insertEntity call.
    ];

    /// When the MainMOC saves - the user has changed data. 
/// We save up into the privateQ as well at this point. 
- (void)mainQueueObjectContextDidSaveNotification:(NSNotification *)notification 
    NSLog(@"main Q MOC has saved - UI level only changes only please");
    [self.privateQueueObjectContext performBlock:^
        NSError* error = nil;
        if (self.privateQueueObjectContext.hasChanges) 
            [self.privateQueueObjectContext save:&error];
            if (error)
            
                NSLog(@"Save up error - the actual datastore was not updated : %@", error);
            
         else 
            //NSLog(@"No need to save");
        
    ];

【讨论】:

【参考方案2】:

关键信息:

其余的上下文将是主队列上下文的子级

因此,您用于处理下载数据的上下文必须是主上下文的子上下文,当您保存该子上下文时,您将保存主上下文,然后保存持久上下文。

当您保存数据处理上下文时,它会立即通知父线程上下文,但您仍然需要 2 次保存才能将数据保存到磁盘上。

【讨论】:

不,反之亦然。 privateMOC 是根,这允许更好的性能,并且出于其他原因也需要这种方式。我只使用了 2 个 MOC,一个是私有根,一个是子线程,一个是主线程。 那么您没有遵循链接到的页面的指导...使用您的方法,您必须在保存后将更改合并到主上下文,它不会自动发生.. . 我明白了 - Marcus Zarra 的页面在根目录下有一个 privateQ。然后他在它下面有一个 mainThreadQ。这就是我正在做的事情。但建议在该主要下方有多个私人,这会在进入系统的大部分数据来自非用户输入的设置中遇到真正的性能问题。 我在 mainQ 上设置了一个通知,这样我就可以知道用户何时编辑了某些内容。这样我就可以将这些编辑反映到云端。在 mainMOC 下的 PrivateMOC 在幕后,数据操作显示为 mainMOC 中的数千条通知。

以上是关于NSManagedObjectContext - 当父母改变时如何更新孩子?的主要内容,如果未能解决你的问题,请参考以下文章

NSManagedObjectContext:撤消保存操作?

声明 NSManagedObjectContext 时出错

CoreData 多 NSManagedObjectContext 保存通知说明

NSManagedObjectContext 类别

NSManagedObjectContext: performBlockAndWait vs performBlock 通知中心

如何清除 NSManagedObjectContext 中的所有对象?