Ios 为什么NSFetchedResultsController';s FetchedObject数组并不总是同构的

Ios 为什么NSFetchedResultsController';s FetchedObject数组并不总是同构的,ios,objective-c,core-data,nsfetchedresultscontroller,nsfetchrequest,Ios,Objective C,Core Data,Nsfetchedresultscontroller,Nsfetchrequest,为了不埋下lede,我将以我的核心问题开始:为什么我的NSFetchedResultsController的FetchedObject数组通常是同质的,但在极少数情况下,它应该包含的托管对象中包含一个\NSCFString 我有一个应用程序已经在生产了很长很长时间。它的主视图是一个包含视频列表的表视图,由核心数据管理对象支持。表视图控制器使用配置了相当普通的NSFetchedResultsController的NSFetchRequest: NSFetchRequest *request = [

为了不埋下lede,我将以我的核心问题开始:为什么我的NSFetchedResultsController的FetchedObject数组通常是同质的,但在极少数情况下,它应该包含的托管对象中包含一个
\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
fetchedObjects
数组来查找与通知对应的视频,以便正确的表视图单元格可以更新其进度条

这一切都有效。99.9%的时间,它每次都能工作

但我在我们的HockeyApp崩溃报告中注意到,有一种罕见的情况,当我的通知处理程序试图从
获取对象
filteredarrayingpredicate
中获取
filteredaryingpredicate
时,我会收到SIGABRT:

***由于未捕获的异常“NSUnknownKeyException”而终止应用程序,原因:“[valueForUndefinedKey:]:此类不符合密钥guid的键值编码要求。”

最近,我设法找到了一个偶尔可以重现此崩溃的案例,经过大量实验后,我发现
获取的对象
数组有时包含的内容不是
ABCVideo
:相反,数组中的一个插槽被
NSCFString
实例占用。考虑到
NSFetchRequestResultType
NSManagedObjectResultType
,并且字符串不是托管对象,这是相当令人惊讶的


所以我想知道:这是一个核心数据错误吗?或者我的数组是否包含一个指针,该指针以前指向一个被释放的
ABCVideo
实例,而堆上的该位置随后被
\NSCFString
实例占用?如果是后者,那么这怎么会发生呢?我使用的是ARC,因此很难理解其中一个视频是如何被释放的。

使用
NSFastEnumeration的
-[NSFetchedResultsController FetchedObject]
中存在内存管理错误。对象0x136f24480是ABCVideo,但已解除分配。那块内存被用来存储字符串。向错误的对象发送消息、EXC_BAD_ACCESS和SIGABRT是常见的结果

这个bug也出现在我的应用程序中,但我一次也无法重现。如果您愿意分享一个可以重现问题的示例项目,我们可以一起解决它

有多种变通方法。关键是避免
NSFastEnumeration
on
fetchedObject

// 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
//1
NSArray*fetchedObjects=controller.fetchedObjects
for(int i=0;i

如果不幸的是,有人正在使用Swift阅读本文,那么即使调用
fetchedObjects
,您也会遇到崩溃,因为Swift使用
NSFastEnumeration
NSArray
转换为
Array

我怀疑是核心数据错误。iOS9引入了几个bug,特别是在NSFetchedResultsController中,这可能是另一个bug。数组本身应该保留它的对象,因此除非您以某种方式释放了太多的对象,否则它们不应该被释放。此外,对于ARC,几乎肯定不是这样。可能是ARC错误或核心数据错误。您能否排除这与使用NSFetchedResultsController进行缓存有关?禁用缓存和复制?因为您可以复制它,所以检测何时发生这种情况并检查字符串的内容可能会很有用。我不知道为什么会有这个字符串,但它的内容可能指向一个原因。最有可能的是,有一个指针曾经指向NSManagedObject(子类),现在指向内存中被NSString对象占用的位置。真正的问题是:您在哪里访问这个指针,为什么它不再指向NSManagedObject?您正在跨线程使用此阵列吗?你把自己的推荐信放在什么地方了吗?你能分享更多关于到底发生了什么的信息吗?对我来说,这似乎不是一个核心数据错误。@JanGreve设置每次复制尝试都需要一段时间,并且不会每次都出现错误,但我可以设置9次尝试(5次禁用缓存,4次启用缓存)。即使没有缓存,我也会崩溃,尽管它会改为EXC_BAD_ACCESS而不是上面的SIGABRT。(遗憾的是,如果我启用僵尸对象,我根本无法重现问题。)这并不能真正回答问题。如果您有不同的问题,可以单击以提问。你也可以提醒更多人注意这个问题@克丽丝:我不知道你从哪里来。作为问题的作者,我认为这是对我的问题的回答,如果它是真的。(为什么数组不是同质的?因为指向已发布对象的伪指针恰好指向具有其他类型的对象)。此错误自iOS 8.x以来就存在。我不确定是否有人