问题
I'm using NSManagedObjectContextObjectsDidChangeNotification and I have a problem where after inserting an object and then calling rollback so it is deleted, the change notification with the deleted object is called twice. This unexpected notification causes complications down the line that I have traced to this issue. I've edited Apple's Earthquakes example to demonstrate this problem. Edits are:
- (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);
}
}
Running this results in the following output:
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
Sample project available here.
Does anyone know why this would happen? I'm experiencing it on both OS X 10.11.2 and iOS 9.2.
回答1:
I see the same behavior and I don't know why it's happening. However, it appears that you can distinguish the first call from the second by looking at the hasChanges
property of the managed object. On the first call, hasChanges
is YES, and on the second call it's NO. That should, at least, be enough to avoid processing the delete yourself twice.
The same change happens on the object's deleted
property, so it might be best to check both.
回答2:
rollback
makes a call to processPendingChanges
and the first notification is a result of the object being deleted, and seems to have all the other values as expected (inserted
and deleted
are both true).
The second notification is a result of processPendingChanges
being called a second time in the implementation of rollback
. Note that it's not even marked as deleted
this time around.
At first, I thought it may be due to deletion propagation, but the second notification happens even if propagatesDeletesAtEndOfEvent
is set to NO.
The following trace is just of the rollback
method (I removed all method calls except those on NSManagedObjectContext
to keep it short - I also removed calls to retain/release).
There is probably a good reason to make the second call to processPendingChanges
because a fair amount of work happens there (see the end of this post).
If I had to make a guess, I would say that there is a bug in the implementation of rollback, where deleted objects are not being removed from the data for the second notification.
I recommend two courses of action. First, file a bug report with apple. Second, when you process object change events, check the deleted
attribute of objects in the deleted set of the notification, and ignore any that are not marked as deleted
.
Here is the trace...
- 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:
来源:https://stackoverflow.com/questions/34426719/why-is-nsmanagedobjectcontextobjectsdidchangenotification-called-twice-with-the