NSFetchedResultsController返回幽灵记录

克雷格·摩尔

几个月来,我一直感到困扰,我终于需要解决这个问题,并一劳永逸地上床睡觉。

我有一个由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] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章