如何跟踪/观察子图中的所有更改?

58

我有一个NSManagedObjectContext,其中包含一些NSManagedObject的子类,其中一些是其他对象的容器。 我想要做的是监视顶级对象,以便在该对象的任何属性、关联或其包含的任何对象的属性/关联发生更改时得到通知。

使用上下文的“hasChanges”方法不能给我足够的细粒度。 对象的“isUpdated”方法仅适用于特定的对象(而不是其关联中的任何内容)。 是否有一种方便的(也许是基于KVO的)方法可以观察仅限于子图的上下文中的更改?

3个回答

129

您需要监听NSManagedObjectContextObjectsDidChangeNotification来捕获数据模型的所有更改。可以使用以下代码实现:

[[NSNotificationCenter defaultCenter] 
      addObserver:self 
         selector:@selector(handleDataModelChange:) 
             name:NSManagedObjectContextObjectsDidChangeNotification 
           object:myManagedObjectContext];

myManagedObjectContext上下文发生任何更改时,将触发-handleDataModelChange:

您的-handleModelDataChange方法可能如下所示:

- (void)handleDataModelChange:(NSNotification *)note
{
    NSSet *updatedObjects = [[note userInfo] objectForKey:NSUpdatedObjectsKey];
    NSSet *deletedObjects = [[note userInfo] objectForKey:NSDeletedObjectsKey];
    NSSet *insertedObjects = [[note userInfo] objectForKey:NSInsertedObjectsKey];

    // Do something in response to this
}

从通知中可以看到有哪些托管对象被更新、删除和插入。根据这些信息,您应该能够响应数据模型的变化。


4
如果你使用NSPredicate来过滤这些对象,实际上它的性能相当不错,因为所有数据都在内存中。我曾在多个iPhone应用程序中使用这种解决方案,但这个过程中还没有出现性能瓶颈。 - Marcus S. Zarra
4
您也可以使用通知NSManagedObjectContextDidSaveNotification在每次保存上下文后过滤更改。 - krasnyk
5
注意:NSNotification的userInfo字典中的对象实际上是 NSSet 类型,而不是 NSArray 类型! - rluba
6
你没有提到这一点,但是为了清晰起见,我想指出:你不能使用此通知向对象发出保存请求。该通知是在 processPendingChanges 处理更改之后发布的,但在调用 save: 方法之前发布。请注意不要混淆。 - memmons
1
如果您正在使用MagicalRecord //监听ROOT SavingContext中的更改 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDataModelChange:) name:NSManagedObjectContextObjectsDidChangeNotification object:[NSManagedObjectContext MR_rootSavingContext]]; - Mohamed Saleh
显示剩余6条评论

19

这是Swift的一个简单示例:

    NotificationCenter.default.addObserver(forName: .NSManagedObjectContextObjectsDidChange, object: nil, queue: nil) { note in
        if let updated = note.userInfo?[NSUpdatedObjectsKey] as? Set<NSManagedObject>, updated.count > 0 {
            print("updated: \(updated)")
        }

        if let deleted = note.userInfo?[NSDeletedObjectsKey] as? Set<NSManagedObject>, deleted.count > 0 {
            print("deleted: \(deleted)")
        }

        if let inserted = note.userInfo?[NSInsertedObjectsKey] as? Set<NSManagedObject>, inserted.count > 0 {
            print("inserted: \(inserted)")
        }
    }

3
通常情况下,我们需要跟踪返回值 let observation = NSNotificationCenter.defaultCenter().addObserverForName(..){..},以便通过调用 NSNotificationCenter.defaultCenter().removeObserver(observation) 停止观察。 - Lev Landau
1
@DanBeaulieu,如果您使用了基于块的版本,NSNotificationCenter.defaultCenter().addObserverForName(..){‌​..},您需要保留返回的观察对象。 - Lev Landau
@LevLandau 我会撤销我的编辑并阅读相关资料,感谢您的提醒。 - Dan Beaulieu

0

对我来说,只是迷失了以下两个函数,也许这可以为某些人节省几个小时

func controllerWillChangeContent(controller: NSFetchedResultsController) {
    tableView.beginUpdates()
}

func controllerDidChangeContent(controller: NSFetchedResultsController) {
    tableView.endUpdates()
}

2
这与问题无关。 - rmaddy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接