几个月来,我一直感到困扰,我终于需要解决这个问题,并一劳永逸地上床睡觉。
我有一个由NSFetchedResultsController提供的表格视图。这是附加到核心数据上的。我使用单独的类来填充Web服务中的核心数据。
这是场景:加载表,NSFetchedResultsController从NSManagedObjectContext返回10行。
您编辑记录1。
然后,您向数据库类发出要进行更新的信号。这些类上载更改后的记录,然后要求Web服务中自上次更新以来已更改的所有记录。此结果集包括更新的记录1。我的类从NSManagedObjectContext删除记录1,然后插入从Web服务下载的新记录1并提交更改。
数据库类现已完成。
tableView现在需要更新。NSFetchedResultsController执行新的提取。NSManagedObjectContext返回10条记录,包括从Web服务下载的新记录1,记住上载的旧记录1已被删除。
现在,我们打开记录2或添加新记录,任何事情都可以访问表中的记录。
现在,任何尝试向NSFetchedResultsController询问记录列表的方法(例如[self.tableView reloadData]
现在返回11条记录,NSManagedObjectContext中存在的10条记录,以及我们删除的旧记录1条。)这一点将检查NSManagedObjectContext并确认:
NSManagedObjectContext包含10条记录。NSFetchedResultsController包含11条记录。
self.NSFetchedResultsController = nil
数据库类从更新NSManagedObjectContext返回后立即使用,以尝试刷新FetchedResultsController,并立即执行新的访存操作(毕竟,在此之后它确实正确返回了10条记录),但在返回之后仍返回了11条记录您尝试更改或访问任何记录。
有人知道这个幽灵记录来自何处吗?它似乎确实来自NSFetchedResultsController,因为NSManagedObjectContext绝不会包含11条记录,并且没有fetch命令会返回11条记录。
如果您尝试打开此幻影记录,则由于系统试图实现显然不存在的故障,您将获得断言失败。
对于任何有兴趣的人来说,这就是从Web服务加载数据的方式:UITableViewController实例化一个负责数据存储的类,称为datastoreSync。然后我们创建一个后台线程,在此后台线程中,我们从原始实例创建一个新的NSManagedObjectContext实例,然后将其分配给datatoreClass并设置委托以及来自NSManagedObjectContext的通知,该通知在更新等之后与主线程的NSManagedObjectContext同步。
完成此操作后,datastoreSync类将在过程结束时引发一个委托,以通知我们更新已完成。为了解决上述问题,我尝试在此时使NSFetchedResultsController无效并触发tableview重载,但尚未解决问题。
根据要求,这里是NSFetchedResultsController的代码
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"ShiftLog" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"logNumber" ascending:NO];
NSSortDescriptor *sortDescriptorProgress = [[NSSortDescriptor alloc] initWithKey:@"pendingUpload" ascending:NO];
NSSortDescriptor *sortDescriptorComplete = [[NSSortDescriptor alloc] initWithKey:@"complete" ascending:YES];
NSArray *sortDescriptors = @[sortDescriptorProgress, sortDescriptorComplete, sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// Load an externally derived asset if needed.
NSPredicate *predicate = [self getFilterPredicate];
if(predicate !=nil){
[fetchRequest setPredicate:predicate];
}
// Remember the request.
_request = fetchRequest;
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
if (self.fetchedResultsController.fetchedObjects == nil){
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
DLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}else{
DLog(@"Caught a populated fetchresultscontroller");
}
return _fetchedResultsController;
}
根据要求,这里是您可以获得的故障的副本
由于未捕获的异常'NSObjectInaccessibleException'而终止应用程序,原因:'CoreData无法为'0x9b5a7c0履行错误' *第一个抛出调用堆栈:(0 CoreFoundation 0x02ad25e4exceptionPreprocess + 180 1 libobjc.A.dylib 0x026698b6 objc_exception_throw + 44 2 CoreData 0x0233f33b _PFFaultHandlerLookupRow + 2715 3 CoreData 0x0233e897 - [NSFaultHandler fulfillFault:withContext:forIndex:] + 39 4 CoreData 0x0233e473 _PF_FulfillDeferredFault + 259 5 CoreData 0x0233e2c6 _sharedIMPL_pvfk_core + 70 6 CoreData 0x0234a4d0 _pvfk_7 + 32 7 [项目名称] 0x00040170-[DetailedViewController configureView] + 336 8 [项目名称] 0x0003dfcb-[DetailedViewController setShiftLogObject:] + 331 9 [项目名称] 0x000063ac-[MasterViewController alertView:clickedButtonAtIndex:] + 828 10 UIKit 0x01349ef3-[UIAlertView(Private )modalItem:tappedButtonAtIndex:] + 67 11 UIKit 0x01417785-[_ UIModalItemsCoordinator _notifyDelegateModalItem:tappedButtonAtIndex:] + 180 12 UIKit 0x00f7305b-[_ UIModalItemAlertContentView tableView:didSelectRowAtIndexPath:] + 380 13 UIKit 0x00f4a7b1-[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 1513 14 UIKit 0x00f4a924-[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 279 15 UIKit 0x00_v_e_block_in_eses_in_eses_in_Event_in_Events___38 in 0_00_4_block_in_eses___38 + 15 17 UIKit 0x00e8512e _applyBlockToCFArrayCopiedToStack + 403 18 UIKit 0x00e84f5a _afterCACommitHandler + 532 19 CoreFoundation 0x02a9a4ce __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION] _block_invoke + 43 16 UIKit 0x00e85183 ___afterCACommitHandler_block_invoke + 15 17 UIKit 0x00e8512e _applyBlockToCFArrayCopiedToStack + 403 18 UIKit 0x00e84f5a _afterCACommitHandler + 532 19 CoreFoundation 0x02a9OUT_UNC_OB_TO_CAL_LING_ALL_CAL_OB_TO_CAL_ALL_TO_CAL_OBL_TO_CAL_OB_TO_CAL] _block_invoke + 43 16 UIKit 0x00e85183 ___afterCACommitHandler_block_invoke + 15 17 UIKit 0x00e8512e _applyBlockToCFArrayCopiedToStack + 403 18 UIKit 0x00e84f5a _afterCACommitHandler + 532 19 CoreFoundation 0x02a9OUT_UNC_OB_TO_CAL_LING_ALL_CAL_OB_TO_CAL_ALL_TO_CAL_OBL_TO_CAL_OB_TO_CAL + 30个20的CoreFoundation 0x02a9a41f __CFRunLoopDoObservers + 399 21的CoreFoundation 0x02a78344 __CFRunLoopRun + 1076 22的CoreFoundation 0x02a77ac3 CFRunLoopRunSpecific + 467 23的CoreFoundation 0x02a778db CFRunLoopRunInMode + 123个24 GraphicsServices 0x034e09e2 GSEventRunModal + 192 25 GraphicsServices 0x034e0809 GSEventRun + 104 26的UIKit 0x00e68d3b UIApplicationMain + 1225 27 [项目] 0x0000258d主+ 141 28 libdyld.dylib 0x0397970d start +1)libc ++ abi.dylib:以类型为_NSCoreDataException的未捕获异常终止
好吧,这是一个奇怪的问题,但是我已经做到了现在需要做的事情。当我开始后台任务更新时,我将其传递给它自己的NSManagedObjectContext。我在此NSManagedObjectContext中将主线程设置为save事件的订阅者,以便在后台MOC执行保存操作时可以更新主线程MOC。
- (void)mergeChanges:(NSNotification *)notification
{
DLog(@"Merge changes has begun");
// Merge changes into the main context on the main thread
NSManagedObjectContext *incommingContext = [notification object];
if (incommingContext != self.managedObjectContext){
dispatch_sync(dispatch_get_main_queue(), ^{
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
});
}
//[self compareMOCs];
}
当您执行操作时NSManagedObjectContext rollback
,NSManagedObjectContext不会回滚(因为已保存),但是NSFetchedResultsController会回滚并显示两个已删除的记录。
要解决此问题,我必须在保存合并后将主线程NSManagedObjectContext保存在主线程中,如下所示:
- (void)mergeChanges:(NSNotification *)notification
{
DLog(@"Merge changes has begun");
// Merge changes into the main context on the main thread
NSManagedObjectContext *incommingContext = [notification object];
if (incommingContext != self.managedObjectContext){
dispatch_sync(dispatch_get_main_queue(), ^{
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
// no idea why I have to do this but if I do not do this any
// attempt to rollback the MOC will cause the NSFetchedResultsController to pull out
// ghost records.
[self.managedObjectContext save:nil];
});
}
//[self compareMOCs];
}
根据苹果公司的说法,这不是必需的,因为仅在后台MOC上发生保存事件之后才触发通知。我无法解释为什么会发生这种情况,我只能告诉您它正在发生,这种额外的节省已解决了它。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句