3

首先,很抱歉我无法为这个问题找到更好的标题。

这是一个我无法重复自己但多次发生在用户身上的崩溃。我正在使用 HockeyApp( QuincyKit ) 收集崩溃报告。所以无法调试这个问题,只能读取调用堆栈。但是读完之后我不知道出了什么问题。我在中间省略了几个不相关的函数调用。

对于 UI 部分,我NSFetchedResultsController用来填充UITableView.

对于核心数据部分,我使用 iOS 5 新引入的父子 MOC:父 MOC 用于主队列,子 MOC 用于资源获取队列。我在处理其中获取的数据后保存子 MOC。一旦我保存子 MOC,Core Data 会自动将更改推送到父 MOC,FRC 应该通过其委托查看更改并更新表视图。然后我回调主线程以进行额外的 UI 更新。

具体来说,我可以理解如何[UITableView indexPathForRowAtPoint:]调用[NSFetchedResultsController objectAtIndexPath:],但我不明白为什么它最终会调用我的实体initWithEntity:insertIntoManagedObjectContext:以及为什么initWithEntity:insertIntoManagedObjectContext:会导致未实现的错误。我认为[NSFetchedResultsController objectAtIndexPath:]应该这样做fetch,但它确实insert在这里。

Exception Type:  SIGABRT
Exception Codes: #0 at 0x35e7e32c
Crashed Thread:  0

Application Specific Information:
*** Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0xe62bdc0 <x-coredata://327F5799-CAB1-4086-8E75-0E87F0E19BF0/Status/p385>''

Last Exception Backtrace:
0   CoreFoundation                      0x355c888f __exceptionPreprocess + 162
1   libobjc.A.dylib                     0x3796f259 objc_exception_throw + 32
2   CoreData                            0x35f124f3 _PFFaultHandlerLookupRow + 1098
3   CoreData                            0x35f11d5b _PF_FulfillDeferredFault + 194
4   CoreData                            0x35f11c0b _sharedIMPL_pvfk_core + 38    
5   XXX                                0x0008a5c3 -[Tweet initWithEntity:insertIntoManagedObjectContext:] (Tweet.m:125)
6   XXX                                0x0003fd1b -[Status initWithEntity:insertIntoManagedObjectContext:] (Status.m:333)
7   CoreData                            0x35f0eded -[NSManagedObject(_NSInternalMethods) _initWithEntity:withID:withHandler:withContext:] + 164
8   CoreData                            0x35f0de07 -[NSManagedObjectContext(_NSInternalAdditions) _retainedObjectWithID:optionalHandler:withInlineStorage:] + 134
9   CoreData                            0x35f6b6eb _PFRetainedObjectIDCore + 330
10  CoreData                            0x35f67065 -[NSManagedObjectContext objectWithID:] + 88
11  CoreData                            0x35f32c09 _faultBatchAtIndex + 1352
12  CoreData                            0x35f31e73 -[_PFBatchFaultingArray objectAtIndex:] + 42
13  CoreData                            0x35f30a87 -[_PFMutableProxyArray objectAtIndex:] + 82
14  CoreData                            0x35fdd81d -[NSFetchedResultsController objectAtIndexPath:] + 204
16  XXX                                0x0009ebab -[TweetsViewController tableView:heightForRowAtIndexPath:] (TweetsViewController.m:581)
17  UIKit                               0x3304dab5 -[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 2548
18  UIKit                               0x33067c3b -[UITableViewRowData rectForSection:] + 302
19  UIKit                               0x33125981 -[UITableViewRowData indexPathsForRowsInRect:] + 136
20  UIKit                               0x331258f1 -[UITableView indexPathsForRowsInRect:] + 52
21  UIKit                               0x33125889 -[UITableView indexPathForRowAtPoint:] + 28
22  XXX                                0x0009c193 -[TweetsViewController updateUI] (TweetsViewController.m:211)
25  XXX                                0x00111a6f __block_global_1 (ResourceFetcher.m:68)
26  libdispatch.dylib                   0x34458b87 _dispatch_barrier_sync_f_slow_invoke + 82
27  libdispatch.dylib                   0x34457ee7 _dispatch_main_queue_callback_4CF$VARIANT$mp + 194
28  CoreFoundation                      0x3559b2ad __CFRunLoopRun + 1268
29  CoreFoundation                      0x3551e4a5 CFRunLoopRunSpecific + 300
30  CoreFoundation                      0x3551e36d CFRunLoopRunInMode + 104
31  GraphicsServices                    0x371ba439 GSEventRunModal + 136
32  UIKit                               0x3302acd5 UIApplicationMain + 1080
33  XXX                                0x000044ff main (main.m:16)
34  XXX                                0x00003c50 start + 40

有这种崩溃问题的用户通常会在崩溃前突然看到表格视图从充满单元格变为空。我认为这是一个重要的提示,但我想不出所有数据突然消失的任何原因——我没有移动或删除数据库文件的代码。

4

2 回答 2

3

首先,在查找错误时,没有不相关的函数调用,因此您可能应该使用整个调用堆栈进行编辑...

看起来您正在请求一个对象(请参阅 objectWithID 调用),但是当托管对象上下文去获取它时,它找不到它。

当您有一个单独的线程正在删除对象并且您没有正确管理更改时,通常会发生这种情况。

那么,您是否有任何其他线程正在改变您的核心数据存储的状态?如果是这样,那可能是你的罪魁祸首。

Core Data 并不难……但它确实有几条规则,如果你不遵守,你就会遇到麻烦。

首先,确保您没有从多个线程访问 MOC。

其次,如果多个线程正在更改底层存储,请确保您与所有 MOCS 正确同步 - 使用父/子上下文或查看 DidSave 通知。

编辑

你评论说:

是的,我也怀疑存在一些删除使用中的问题。但首先,你知道为什么这里会调用 initWithEntity:insertIntoManagedObjectContext: 吗?

是的......它需要实例化对象。查看您的调用堆栈,您可以看到它...

您的 UI 正在尝试在主线程上更新...

22  XXX  0x0009c193 -[TweetsViewController updateUI] (TweetsViewController.m:211)

因此,UIKit 会做它的事情,并向您的控制器发出委托回调......

16  XXX  0x0009ebab -[TweetsViewController tableView:heightForRowAtIndexPath:] (TweetsViewController.m:581)

这会导致您正在谈论的 FRC 呼叫...

14  CoreData  0x35fdd81d -[NSFetchedResultsController objectAtIndexPath:] + 204

然后,需要该对象,因此将其取出...

10  CoreData  0x35f67065 -[NSManagedObjectContext objectWithID:] + 88

好吧,即使是 Core Data 也必须实例化该对象。它没有一些神奇的界面。它调用用于创建托管对象的普通 API……显然您正在请求“状态”类型的对象

6   XXX  0x0003fd1b -[Status initWithEntity:insertIntoManagedObjectContext:] (Status.m:333)

反过来,这个对象需要一个“Tweet”类型的对象......

5   XXX  0x0008a5c3 -[Tweet initWithEntity:insertIntoManagedObjectContext:] (Tweet.m:125)

然后,当它必须实例化那个对象时......它失败了,因为它找不到它。因此,我再次打赌,您的同步中存在逻辑错误。您正在删除一个对象并且没有正确通知其他上下文,因此它们仍在引用已删除的对象......或者您正在引用一个没有真正保存的对象......或者您已经更改了关系,但没有正确通知其他情况。

无论哪种方式,我认为如果您跟踪更改数据库的方式,您将违反一些用于在多个线程中更新的标准协议,这是一个不错的选择。

事实上,你甚至可以看到你是如何到达那里的......

从 GCD 调度队列调用 updateUI。最有可能的是,在 ResourceFetcher 中,您正在调用主线程。在该代码中,请确保您已正确保存上下文并发出所有通知,然后再要求主线程自行更新。

你最好听听来自其他 MOC 的 DidSave 事件,然后在从那个上下文更新之后,调用你的 UI 来更新自己。

于 2012-06-02T23:34:56.647 回答
2

最后,我想我已经修复了崩溃,尽管我不确定这是我的错误还是 Core Data 的错误。

首先,它与 MOC 之间的同步无关。

其次,是的,initWithEntity:insertIntoManagedObjectContext:当对象第一次在 moc 中实例化时调用,无论它们是新创建的还是通过请求获取的。因此,该文档似乎具有误导性:

如果 context 不是 nil,则此方法调用 [context insertObject:self](这会导致调用 awakeFromInsert)。

如果调用它来获取,awakeFromFetch则调用它。

正如您在回溯中看到的那样,崩溃发生在我被覆盖的initWithEntity:insertIntoManagedObjectContext:. 足够合理,initWithEntity:insertIntoManagedObjectContext:原来是坠机的原因。但我不认为我在这件事上做错了什么。我只是使用了一些持久化属性来初始化一些非持久化属性:

- (id)initWithEntity:(NSEntityDescription *)entity insertIntoManagedObjectContext:(NSManagedObjectContext *)context {
    self = [super initWithEntity:entity insertIntoManagedObjectContext:context];
    if (self) {
        // xxx_ is a persistent attribute while _xxx is the corresponding non-persistent attribute as its cache in memory only to reduce reading cost.
        _xxx = self.xxx_.unsignedIntegerValue;
    }
    return self;
}

有时,当我阅读第一个持久属性时,会抛出无法实现的异常。

我一直都知道

不鼓励您重写此方法——您应该重写 awakeFromInsert 和/或 awakeFromFetch(如果这些方法有共同的逻辑,则应将其分解为从两者调用的第三个方法)。如果您确实在此方法中执行自定义初始化,则可能会导致撤消和重做操作出现问题。

但我不知道这会导致无法实现的异常。它是核心数据错误吗?否则,我的代码有什么问题?

于 2012-06-11T20:03:01.950 回答