首先,对不起,我无法为这个问题找到更好的标题。
这是一次崩溃,我不能重复,但很多次发生在用户身上。我正在使用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
遇到此崩溃问题的用户通常会在崩溃前看到表格视图从完整单元格变为空白。我认为这是重要提示,但我想不出有什么原因导致所有数据突然消失 - 我没有代码来移动或删除数据库文件。
答案 0 :(得分:3)
首先,在查找错误时,没有不相关的函数调用,所以你应该用整个调用堆栈进行编辑......
看起来您正在请求一个对象(请参阅objectWithID调用),但是当托管对象上下文获取它时,它无法找到它。
当你有一个单独的线程正在删除对象,并且你没有正确管理这些更改时,通常会发生这种情况。
那么,您是否有其他线程正在改变核心数据存储的状态?如果是这样,那可能是你的罪魁祸首。
核心数据并不难......但它确实有一些规则,如果你不遵循,你就会陷入困境。
首先,确保您不从多个线程访问MOC。
其次,如果多个线程正在更改底层存储,请确保您正确地与所有MOCS同步 - 使用父/子上下文或观看DidSave通知。
修改强>
您评论道:
是的,我也怀疑有一些删除 - 使用中的东西 问题。但首先,你知道为什么吗? initWithEntity:insertIntoManagedObjectContext:在这里调用吗?
是的......它需要实例化对象。看看你的调用堆栈,你可以看到它......
您的用户界面正在尝试更新主线程...
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 ...显然,您正在请求“Status”类型的对象
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进行自我更新。
答案 1 :(得分:2)
最后,我想我已经修复了崩溃但我不确定这是我的错误还是Core Data的错误。
首先,它与MOC之间的同步无关。
其次,是的,在moc中首次实例化对象时调用initWithEntity:insertIntoManagedObjectContext:
,无论它们是新创建的还是使用请求获取的。因此,似乎该文档具有误导性:
如果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(如果有逻辑的话) 对于这些方法来说,它应该被考虑到第三种方法中 从两者调用。如果您执行自定义初始化 在此方法中,您可能会导致撤消和重做操作出现问题。
但我不知道这会导致无法弥补的异常。这是一个核心数据错误吗?否则,我的代码出了什么问题?