所以我没有埋葬lede,我将打开我的核心问题:为什么我的NSFetchedResultsController的fetchedObjects数组通常是同类的,但在极少数情况下在管理中包含__NSCFString
它应该包含哪些对象?
我有一个已经投入生产了很长时间的应用程序。它的主要视图是一个表视图,其中包含由核心数据管理对象支持的视频列表。表视图控制器使用配置了相当普通的NSFetchedResultsController
:
NSFetchRequest
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:[ABCVideo entityName]];
NSString *sectionKeyPath = nil;
request.fetchBatchSize = 20;
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:ABCVideoAttributes.recordingDate ascending:NO];
sectionKeyPath = @"sectionIdentifier";
request.sortDescriptors = @[sort];
request.predicate = [NSPredicate predicateWithFormat:@"owner = %@ and %K = %@", person, ABCVideoAttributes.serverDeleted, @(NO)];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:moc sectionNameKeyPath:sectionKeyPath cacheName:kABCMyVideosTableViewControllerCacheKey];
由于这些视频可以上传到云端,因此该表格视图控制器会偶尔收到通知,以更新与当前正在上传的视频对应的表格视图单元格中的进度条。在此回调中,我们获取NSFetchedResultsController
的{{1}}数组以查找与通知对应的视频,以便正确的表格视图单元格可以更新其进度条。
这一切都有效。 99.9%的时间,它每次都有效fetchedObjects
。
但是我在HockeyApp崩溃报告中注意到,在我的通知处理程序试图从</RonBurgundy>
获取filteredArrayUsingPredicate
时,我遇到了一个罕见的罕见情况: / p>
fetchedObjects
最近我设法找到一个案例,我偶尔可以重现这次崩溃,经过大量的实验,我发现*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFString 0x136f24480> valueForUndefinedKey:]: this class is not key value coding-compliant for the key guid.'
数组有时会包含一些不是fetchedObjects
的内容:而是,数组中的一个插槽被ABCVideo
实例占用。鉴于__NSCFString
是NSFetchRequestResultType
,并且字符串不是托管对象,这非常令人惊讶。
所以我想知道:这是一个核心数据错误吗?或者我的数组是否包含一个指针,该指针以前指向已释放的NSManagedObjectResultType
实例,并且堆上的位置随后由ABCVideo
实例获取?如果是后者,那怎么会发生这种情况呢?我正在使用ARC,因此很难理解其中一个视频是如何解除分配的。
答案 0 :(得分:5)
-[NSFetchedResultsController fetchedObjects]
NSFastEnumeration
存在内存管理错误。对象0x136f24480是ABCVideo但已取消分配。那段内存用于存储__NSCFString。将消息发送到错误的对象,EXC_BAD_ACCESS和SIGABRT是常见的结果。
这个错误也在我的应用程序中,但我甚至无法重现它。如果您愿意分享可以重现问题的示例项目,我们可以共同努力解决问题。
有多种解决方法。关键是要避免NSFastEnumeration
fetchedObjects
// 1
NSArray *fetchedObjects = controller.fetchedObjects
for (int i = 0; i < fetchedObjects.count; ++i) {
NSManagedObject *object = fetchedObjects[i];
}
// 2
NSArray <id<NSFetchedResultsSectionInfo>> *sections = controller.sections;
for (int s = 0; s < sections.count; ++s) {
id<NSFetchedResultsSectionInfo> section = sections[s];
for (int i = 0; i < [section numberOfObjects]; ++i) {
NSManagedObject *object = [controller objectAtIndexPath:[NSIndexPath indexPathForItem:i inSection:s]];
}
}
// 3 Fetch from NSManagedContextDirectly
如果不幸的是,有人在阅读此内容时使用的是Swift,即使调用fetchedObjects
,您也会崩溃,因为Swift会使用NSArray
将Array
转换为NSFastEnumeration
。