Почему NSManagedObjectContextObjectsDidChangeNotification вызывается дважды с удалёнными объектами после откат?
Я использую NSManagedObjectContextObjectsDidChangeNotification, и у меня возникла проблема, когда после вставки объекта и последующего вызова отката, чтобы он был удален, уведомление об изменении с удаленным объектом вызывается дважды. Это неожиданное уведомление вызывает осложнения по линии, которую я проследил по этой проблеме. Я продемонстрировал пример Apple Earthquakes, чтобы продемонстрировать эту проблему. Редактирование:
- (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
Пример доступного проекта здесь.
Кто-нибудь знает, почему это произойдет? Я испытываю это как на OS X 10.11.2, так и на iOS 9.2.
Ответы
Ответ 1
Я вижу такое же поведение, и я не знаю, почему это происходит. Однако кажется, что вы можете отличить первый вызов от второго, посмотрев на свойство hasChanges
управляемого объекта. При первом вызове hasChanges
есть ДА, а во втором вызове НЕТ. Этого должно быть, по крайней мере, достаточно, чтобы не дважды обрабатывать удаление.
То же самое происходит и с объектным свойством deleted
, поэтому лучше проверить оба.
Ответ 2
rollback
выполняет вызов processPendingChanges
, и первое уведомление является результатом удаляемого объекта и, как представляется, имеет все остальные значения, как ожидалось (inserted
и deleted
оба значения true).
Второе уведомление является результатом второго вызова processPendingChanges
в реализации rollback
. Обратите внимание, что на этот раз он не помечен как deleted
.
Сначала я думал, что это может быть связано с распространением делеции, но второе уведомление происходит, даже если для параметра propagatesDeletesAtEndOfEvent
установлено значение NO.
Следующая трассировка относится только к методу rollback
(я удалял все вызовы методов, кроме тех, что на NSManagedObjectContext
, чтобы он был коротким - я также удалял вызовы для сохранения/выпуска).
Вероятно, есть веская причина сделать второй вызов processPendingChanges
, потому что там происходит довольно много работы (см. конец этого сообщения).
Если бы я должен был предположить, я бы сказал, что есть ошибка в реализации отката, где удаленные объекты не удаляются из данных для второго уведомления.
Я рекомендую два курса. Сначала напишите отчет об ошибке с помощью apple. Во-вторых, при обработке событий изменения объектов проверяйте атрибут 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: