我有一个这样的核心数据模型设置:
TileList <->> TileListOrder
TileListOrder <<-> Tile
并且创建了一个 NSFetchedResultsController:
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"tile <> nil AND tileList <> nil AND tile.removed == 0 AND tileList.removed == 0"];
NSSortDescriptor* sortTileListDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"tileList.order" ascending:YES] autorelease];
NSSortDescriptor* sortTileDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"order" ascending:YES] autorelease];
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:sortDescriptors];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"TileListOrder" inManagedObjectContext:coreManagedObjectContext];
[fetchRequest setEntity:entity];
NSFetchedResultsController* fetchedResults = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:coreManagedObjectContext
sectionNameKeyPath:@"tileList.order"
cacheName:nil];
我还在辅助 NSManagedObjectContext 上导入数据并将这些更改合并回来,如下所示:
- (void) localContextDidSave:(NSNotification*) notification {
dispatch_sync(dispatch_get_main_queue(), ^{
[coreManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
[self didSaveModelToCoreDataPersistentStore];
});
[super didEndUpdatingObjectInBackgroundThread];
}
我正在添加和删除 TileListOrders 对象。它从 6 个对象和 3 个部分开始,当我完成时,删除了 6 个原始对象并插入了 6 个新对象。
现在我的问题是在 NSFetchedResultsController 委托上,它在 mergeChangesFromContextDidSaveNotification:notification 之后立即被调用
-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller{
NSLog(@"controller has %d sections, %d results:",[[controller sections] count], [[controller fetchedObjects] count]);
}
第一次打印 12 个对象和 3 个部分时被称为 TWICE,第二次在 3 个部分中打印 6 个对象。
在检查 fetchedObjects 时,第一次有 6 个插入的对象加上其他 6 个被删除的原始对象。第二次它包含预期的 3 个部分中的 6 个对象。
看着:
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath{
}
第一次调用 controllerDidChangeContent 时,仅处理 NSFetchedResultsChangeInsert 更改,第二次仅处理 NSFetchedResultsChangeDelete 更改。
我希望 controllerDidChangeContent 只有在处理完所有更改后才会被调用。如果这是驱动一个 tableView 那么这意味着 tableview 将动画两次。一次用于插入的项目,另一个用于已删除的项目。问题是,第一次调用 controllerDidChangeContent 时,NSFetchedResultsController 中的对象已被删除,但仍存在于结果数组中,现在它们的属性值无效,因此显示这些项目的 tableView 可能会崩溃。
任何人都知道如何使更改合并,以便在收到 mergeChangesFromContextDidSaveNotification 通知后仅调用一次 controllerDidChangeContent 吗?
-----------更新---------------
更多信息。
我只是在观察 NSManagedObjectContextDidSaveNotification 并且 localContextDidSave 只被调用一次。
NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self selector:@selector(localContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:managedObjectContext];
这是堆栈跟踪。第一次调用 controllerDidChangeContent 时,仅报告与 INSERTED 对象相关的更改,并且已删除的对象仍然存在于 NSFetchedResultsController 结果中。如您所见,它是由
#0 0x001fb735 in -[HomeViewController controllerDidChangeContent:]
#1 0x02947f9a in -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] ()
#2 0x022d34f9 in __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_0 ()
#3 0x02e1c0c5 in ___CFXNotificationPost_block_invoke_0 ()
#4 0x02d76efa in _CFXNotificationPost ()
#5 0x02207bb2 in -[NSNotificationCenter postNotificationName:object:userInfo:] ()
#6 0x0285a163 in -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] ()
#7 0x0286dc71 in -[NSManagedObjectContext _mergeChangesFromDidSaveDictionary:usingObjectIDs:] ()
#8 0x0286ce2e in -[NSManagedObjectContext mergeChangesFromContextDidSaveNotification:] ()
#9 0x00044be2 in __50-[CoreDataUpdateableObject localContextDidSave:]_block_invoke
#10 0x0306f731 in _dispatch_barrier_sync_f_slow_invoke ()
#11 0x0307e014 in _dispatch_client_callout ()
#12 0x0306e7d5 in _dispatch_main_queue_callback_4CF ()
#13 0x02d68af5 in __CFRunLoopRun ()
#14 0x02d67f44 in CFRunLoopRunSpecific ()
#15 0x02d67e1b in CFRunLoopRunInMode ()
#16 0x038617e3 in GSEventRunModal ()
#17 0x03861668 in GSEventRun ()
#18 0x01756ffc in UIApplicationMain ()
#19 0x000022c6 in main
第二次调用 controllerDidChangeContent 时,堆栈跟踪是:
#0 0x001fb735 in -[HomeViewController controllerDidChangeContent:]
#1 0x02947f9a in -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] ()
#2 0x022d34f9 in __57-[NSNotificationCenter addObserver:selector:name:object:]_block_invoke_0 ()
#3 0x02e1c0c5 in ___CFXNotificationPost_block_invoke_0 ()
#4 0x02d76efa in _CFXNotificationPost ()
#5 0x02207bb2 in -[NSNotificationCenter postNotificationName:object:userInfo:] ()
#6 0x0285a163 in -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] ()
#7 0x028f3d2f in -[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:] ()
#8 0x02855596 in -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] ()
#9 0x02854869 in -[NSManagedObjectContext processPendingChanges] ()
#10 0x02828e38 in _performRunLoopAction ()
#11 0x02d8aafe in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#12 0x02d8aa3d in __CFRunLoopDoObservers ()
#13 0x02d687c2 in __CFRunLoopRun ()
#14 0x02d67f44 in CFRunLoopRunSpecific ()
#15 0x02d67e1b in CFRunLoopRunInMode ()
#16 0x038617e3 in GSEventRunModal ()
#17 0x03861668 in GSEventRun ()
#18 0x01756ffc in UIApplicationMain ()
#19 0x000022c6 in main
这一次它是由:[NSManagedObjectContext processPendingChanges] () 这第二次 NSFetchedResultsController 结果实际上具有所需的状态:删除的对象被删除,插入的对象存在。
我的问题是第一个 mergeChanges 应该触发具有合并更改的 controllerWillChangeContent,因此结果应该处于一致状态。
从发布的原始代码中,我确实添加了 [coreManagedObjectContext processPendingChanges]; [coreManagedObjectContext mergeChangesFromContextDidSaveNotification:notification] 之后;因为没有这个,第二次调用只会在下一次运行循环时触发。
我也试过 [coreManagedObjectContext setPropagatesDeletesAtEndOfEvent:NO]; 但行为是一样的。已删除的对象仅在第二次调用中被删除。