【问题标题】:How to listen for specific entity saves/updates in CoreData如何在 CoreData 中侦听特定实体的保存/更新
【发布时间】:2015-02-26 18:35:03
【问题描述】:

我尝试的第一件事是使用 FetchedResultsController 来解决我的问题。这是 FRC 的一种非典型用法,因为我没有更新 TableView,我只是用它来确定实体是否正在更改,以便我知道何时保存到服务器:

self.fetchedResultsController = [Document MR_fetchAllSortedBy:@"timestamp"
                                                    ascending:NO
                                                withPredicate:[NSPredicate predicateWithFormat:@"report.remoteId != nil && dirty == YES"]
                                                      groupBy:nil
                                                     delegate:self
                                                    inContext:_managedObjectContext];

这里的问题是,当关系实体发生变化时,FRC 不会收到更新。 IE 如果 report.remoteId 从 nil 变为 non-nil,我将看不到更新,因为 FRC 仅侦听 Document 实体上的更改。此处列出的限制:http://www.mlsite.net/blog/?p=825 和此处Changing a managed object property doesn't trigger NSFetchedResultsController to update the table view

不确定我是否真的想要实施该解决方法,因为我觉得我正在使用 FRC 来完成它不应该做的事情。我不认为苹果会解决这个限制,所以我真的不想把一个圆钉砸进一个方孔来解决我的问题。

几个选项

保存实体后,在代码中触发通知,然后在其他地方收听。我唯一不喜欢的一点是程序员必须这样做并保持最新状态,即如果有人出现并将实体保存在另一个地方,他们必须记住触发通知。

监听保存到默认 MOC。这是我真正想做的。像这样的:

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

- (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
}

这是我的困境。这将监听默认 MOC 上的所有更改。我真的只关心对几个实体的更改。所以是的,我可以在每次通话中过滤掉我关心的实体。但是,我有一个案例,其中我保存了大量我不关心的其他实体。这意味着 NSManagedObjectContextObjectsDidChangeNotification 会触发很多,而且大多数时候我不在乎。我不想通过不断收到此通知并花时间过滤掉所有我不关心的实体来减慢我的应用程序。

有没有办法监听特定的实体保存?在 CoreData 还是 MagicalRecord 中?

如果答案是否定的,是否有更好的替代方法来监听特定实体(及其关系)的变化?

【问题讨论】:

    标签: ios objective-c core-data magicalrecord


    【解决方案1】:

    无法监听特定实体集的更改;捕获NSManagedObjectContextObjectsDidChangeNotification(或已经保存或将保存)并过滤是正确的方法,但需要注意的是,如果您正在谈论实体的特定实例,键值观察也是一种选择。

    然而,值得注意的是NSManagedObjectID 是线程安全的,并为NSEntityDescription 提供了一个getter。所以你可以例如

    - (void)handleDataModelChange:(NSNotification *)note {
        NSSet *updatedObjects = [[note userInfo] objectForKey:NSUpdatedObjectsKey];
        NSSet *deletedObjects = [[note userInfo] objectForKey:NSDeletedObjectsKey];
        NSSet *insertedObjects = [[note userInfo] objectForKey:NSInsertedObjectsKey];
    
        NSMutableArray *objectIDs = [NSMutableArray array];
        [objectIDs addObjectsFromArray:[updatedObjects.allObjects valueForKey:@"objectID"]];
        [objectIDs addObjectsFromArray:[deletedObjects.allObjects valueForKey:@"objectID"]];
        [objectIDs addObjectsFromArray:[insertedObjects.allObjects valueForKey:@"objectID"]];
    
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(){
            NSSet *affectedEntities = [NSSet setWithArray:[objectIDs valueForKeyPath:@"entity.name"]];
    
            if([affectedEntities containsObject:@"InterestingEntity"]) {
                // get back onto the main thread and do some proper work;
                // possibly having collated the relevant object IDs here
                // first — whatever is appropriate
            }
        });
    }
    

    【讨论】:

    • 谢谢@Tommy!比重写 NSManagedObject 的 onSave 方法更好/更差?
    • 取决于您的使用情况。如果您尝试捕获实体 Q 的任何实例的所有保存,并且一次保存了 1,000 个实例(可能它们来自 Web 服务或其他地方),那么您只会收到一个通知,但会收到 1,000 个didSaves。我想问题是您是否希望让对象自主行动(例如应用自动更正验证),或者您是在写有人监视它们中的很多(例如,您有一个大而复杂的视图来做某事基于数据的整体,而不是单独显示每个数据)。
    • ... 可能我唯一很难学到的东西是,由于添加和删除观察者的成本,在快速滚动时,像表视图这样的键值观察会产生意想不到的成本,大概是因为从 0 个观察者到 1 个观察者,反之亦然,这是一种方法调配,改变消息映射和某种内部到 KVO 实例映射。在这种情况下,我发现捕获通知、处理并将更改的阻止列表推送到主队列是一个更好的解决方案。
    • 我想我更担心的是我不关心的实体 X 的 60 分钟保存。只有一个可以节省我关心的实体 Y 的一个小时。但有时我会一次获得大量实体 Y 的存档。
    • 托管对象本身不是线程安全的,因此从技术上讲,您至少需要在初始线程上进行那些objectID 调用。否则行为将是未定义的(尽管很难想象重大问题)。我决定把它全部作为数组来做,因为它们比集合更便宜。
    【解决方案2】:

    在阅读了文档和其他一些表格后,我还想出了另一个可能的解决方案。

    为什么不重写托管对象的 -(void) didSave 方法来捕获该对象的更改。

    @implementation Report (helper)
    
    - (void) didSave {
        [super didSave];
    
        // if not on default context no-op
        if ([NSManagedObjectContext MR_defaultContext] != self.managedObjectContext) return;
    
        // send custom notification here  
    }
    
    @end
    

    仍然处理发送自定义通知,但至少封装在托管对象中,这样开发人员不必担心将通知发送到哪里。

    从好的方面来说,这只对我关心的托管对象执行。

    缺点是它被所有托管对象上下文调用。不过我不认为 MOC 检查会是一个沉重的打击。

    不确定这是否比在默认 MOC 上进行保存和过滤更好/更差。另外在这种情况下,我知道我只在那个 MOC 上收听保存。即使我可以在后台任务中过滤,但在这种情况下,我仍然会过滤大量不需要处理的数据。

    想法?

    【讨论】:

    • 改为注册到NSManagedObjectContextDidSaveNotification
    猜你喜欢
    • 1970-01-01
    • 2010-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多