【问题标题】:Why is NSManagedObjectContextObjectsDidChangeNotification called twice with the deleted objects after a rollback?为什么 NSManagedObjectContextObjectsDidChangeNotification 在回滚后被删除的对象调用两次?
【发布时间】:2016-03-29 08:37:55
【问题描述】:

我有一个NSManagedObjectContextObjectsDidChangeNotification 的问题,在插入对象然后执行回滚(因此它被删除)之后,关于删除的更改通知被调用了两次。这个意外的通知给我带来了麻烦,我已经追溯到这个问题。我编辑了Apple's Earthquakes example 来演示这个问题。编辑是:

- (void)viewDidLoad {
    [super viewDidLoad];

    [self reloadTableView:self];
    
    [NSNotificationCenter.defaultCenter
     addObserver:self
     selector:@selector(contextObjectsDidChangeNotification:)
     name:NSManagedObjectContextObjectsDidChangeNotification
     object:self.managedObjectContext];
    
    AAPLQuake* quake = (AAPLQuake *)[NSEntityDescription insertNewObjectForEntityForName:@"Quake" inManagedObjectContext:self.managedObjectContext];
    
    NSLog(@"Why is deleted notified twice?");
    [self.managedObjectContext rollback];
}

- (void)contextObjectsDidChangeNotification:(NSNotification *)notify{
    NSLog(@"contextObjectsDidChangeNotification:");
    NSDictionary* userInfo = notify.userInfo;
    NSSet* inserted = userInfo[NSInsertedObjectsKey];
    if(inserted){
        NSLog(@"\tinserted %ld", inserted.count);
    }
    NSSet *deleted = userInfo[NSDeletedObjectsKey];
    if(deleted){
        NSLog(@"\tdeleted %ld", deleted.count);
    }
}

运行它会产生以下输出:

2015-12-23 00:15:20.086 Earthquakes[7631:5431685] Why is deleted called twice?
2015-12-23 00:15:20.086 Earthquakes[7631:5431685] contextObjectsDidChangeNotification:
2015-12-23 00:15:20.086 Earthquakes[7631:5431685]   inserted 1
2015-12-23 00:15:20.086 Earthquakes[7631:5431685] contextObjectsDidChangeNotification:
2015-12-23 00:15:20.087 Earthquakes[7631:5431685]   deleted 1
2015-12-23 00:15:20.087 Earthquakes[7631:5431685] contextObjectsDidChangeNotification:
2015-12-23 00:15:20.087 Earthquakes[7631:5431685]   deleted 1

提供示例项目here

有人知道为什么会这样吗?我在 OS X 10.11.2 和 iOS 9.2 上都体验过。

【问题讨论】:

  • 你在哪里删除了观察者?
  • 我可以确认我看到了相同的行为,但不确定是什么原因。
  • 感谢您确认您看到了相同的行为。我尝试使用撤消管理器而不是回滚,它没有这个问题。

标签: ios objective-c macos core-data rollback


【解决方案1】:

我看到了同样的行为,但我不知道为什么会这样。但是,您似乎可以通过查看托管对象的 hasChanges 属性来区分第一次调用和第二次调用。在第一次通话中,hasChanges 为“是”,在第二次通话中为“否”。这至少应该足以避免自己处理两次删除。

对象的deleted 属性也会发生同样的变化,因此最好同时检查两者。

【讨论】:

    【解决方案2】:

    rollback 调用processPendingChanges 并且第一个通知是对象被删除的结果,并且似乎具有预期的所有其他值(inserteddeleted 都是真的)。​​

    第二次通知是processPendingChangesrollback 的实现中被第二次调用的结果。请注意,这次它甚至没有被标记为deleted

    一开始我以为可能是因为删除传播,但即使propagatesDeletesAtEndOfEvent设置为NO,第二次通知也会发生。

    以下跟踪只是 rollback 方法的跟踪(我删除了所有方法调用,除了 NSManagedObjectContext 上的方法调用以保持简短 - 我还删除了对保留/释放的调用)。

    第二次致电processPendingChanges 可能是有充分理由的,因为那里发生了大量工作(请参阅本文末尾)。

    如果我不得不猜测,我会说在回滚的实现中存在一个错误,其中删除的对象没有从第二次通知的数据中删除。

    我建议采取两种行动。首先,向苹果提交错误报告。其次,在处理对象变化事件时,检查通知的已删除集中对象的deleted属性,忽略没有标记为deleted的对象。

    这是踪迹……

    - NSManagedObjectContext NSManagedObjectContext rollback
    - NSManagedObjectContext NSManagedObjectContext discardEditing
    - NSManagedObjectContext NSManagedObjectContext isEditing
    - NSManagedObjectContext NSManagedObjectContext propagatesDeletesAtEndOfEvent
    - NSManagedObjectContext NSManagedObjectContext setPropagatesDeletesAtEndOfEvent:
    - NSManagedObjectContext NSManagedObjectContext performBlockAndWait:
    - NSManagedObjectContext NSManagedObjectContext processPendingChanges
    - NSManagedObjectContext NSManagedObjectContext _processRecentChanges:
    - NSManagedObjectContext NSManagedObjectContext _postRefreshedObjectsNotificationAndClearList
    - NSManagedObjectContext NSManagedObjectContext _processReferenceQueue:
    - NSManagedObjectContext NSManagedObjectContext deleteObject:
    - NSManagedObjectContext NSManagedObjectContext _registerClearStateWithUndoManager
    - NSManagedObjectContext NSManagedObjectContext _establishEventSnapshotsForObject:
    - NSManagedObjectContext NSManagedObjectContext _enqueueEndOfEventNotification
    - NSManagedObjectContext NSManagedObjectContext _postObjectsDidChangeNotificationWithUserInfo:
    - NSManagedObjectContext NSManagedObjectContext processPendingChanges
    - NSManagedObjectContext NSManagedObjectContext _processRecentChanges:
    - NSManagedObjectContext NSManagedObjectContext _registerClearStateWithUndoManager
    - NSManagedObjectContext NSManagedObjectContext _updateUnprocessedOwnDestinations:
    - NSManagedObjectContext NSManagedObjectContext _propagatePendingDeletesAtEndOfEvent:
    - NSManagedObjectContext NSManagedObjectContext _processPendingDeletions:withInsertions:withUpdates:withNewlyForgottenList:withRemovedChangedObjects:
    - NSManagedObjectContext NSManagedObjectContext _processPendingInsertions:withDeletions:withUpdates:
    - NSManagedObjectContext NSManagedObjectContext _processPendingUpdates:
    - NSManagedObjectContext NSManagedObjectContext _registerUndoForModifiedObjects:
    - NSManagedObjectContext NSManagedObjectContext _registerUndoForInsertedObjects:
    - NSManagedObjectContext NSManagedObjectContext _registerUndoForDeletedObjects:withDeletedChanges:
    - NSManagedObjectContext NSManagedObjectContext _registerUndoForOperation:withObjects:withExtraArguments:
    - NSManagedObjectContext NSManagedObjectContext _updateUndoTransactionForThisEvent:withDeletions:withUpdates:
    - NSManagedObjectContext NSManagedObjectContext _clearRefreshedObjects
    - NSManagedObjectContext NSManagedObjectContext _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:
    - NSManagedObjectContext NSManagedObjectContext _postObjectsDidChangeNotificationWithUserInfo:
    - NSManagedObjectContext NSManagedObjectContext _processRecentlyForgottenObjects:
    - NSManagedObjectContext NSManagedObjectContext _forgetObject:propagateToObjectStore:
    - NSManagedObjectContext NSManagedObjectContext _forgetObject:propagateToObjectStore:removeFromRegistry:
    - NSManagedObjectContext NSManagedObjectContext _processReferenceQueue:
    - NSManagedObjectContext NSManagedObjectContext _isDeallocating
    - NSManagedObjectContext NSManagedObjectContext _forgetObject:propagateToObjectStore:
    - NSManagedObjectContext NSManagedObjectContext _forgetObject:propagateToObjectStore:removeFromRegistry:
    - NSManagedObjectContext NSManagedObjectContext _resetAllChanges
    - NSManagedObjectContext NSManagedObjectContext _clearUnprocessedUpdates
    - NSManagedObjectContext NSManagedObjectContext _clearUpdates
    - NSManagedObjectContext NSManagedObjectContext _clearUnprocessedInsertions
    - NSManagedObjectContext NSManagedObjectContext _clearInsertions
    - NSManagedObjectContext NSManagedObjectContext _clearUnprocessedDeletions
    - NSManagedObjectContext NSManagedObjectContext _clearDeletions
    - NSManagedObjectContext NSManagedObjectContext _clearLockedObjects
    - NSManagedObjectContext NSManagedObjectContext _clearRefreshedObjects
    - NSManagedObjectContext NSManagedObjectContext _incrementUndoTransactionID
    - NSManagedObjectContext NSObject willChangeValueForKey:
    - NSManagedObjectContext NSObject observationInfo
    - NSManagedObjectContext NSObject _implicitObservationInfo
    - NSManagedObjectContext NSObject didChangeValueForKey:
    - NSManagedObjectContext NSObject _pendingChangeNotificationsArrayForKey:create:
    - NSManagedObjectContext NSManagedObjectContext setPropagatesDeletesAtEndOfEvent:
    - NSManagedObjectContext NSManagedObjectContext performBlockAndWait:
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-02
      • 2011-10-16
      • 1970-01-01
      • 2016-11-23
      • 1970-01-01
      • 2021-07-13
      相关资源
      最近更新 更多