为什么NSFetchedResultsController的fetchedObjects数组并不总是同质的

poh*_*ohl 7 core-data objective-c nsfetchedresultscontroller nsfetchrequest ios

所以我不会埋没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];
Run Code Online (Sandbox Code Playgroud)

由于这些视频可以上传到云端,因此该表格视图控制器会偶尔收到通知,以更新与当前正在上传的视频对应的表格视图单元格中的进度条.在这个回调中,我们得到NSFetchedResultsControllerfetchedObjects数组用于查找与通知相对应的视频,以便正确的表格视图单元格可以更新其进度条.

这一切都有效.99.9%的时间,它每次都有效</RonBurgundy>.

但我注意到在我们的HockeyApp崩溃报告中,有一种罕见的,罕见的情况,当我的通知处理程序试图filteredArrayUsingPredicate从以下位置获取时,我收到SIGABRT 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实例占用.这非常令人惊讶,因为它NSFetchRequestResultTypeNSManagedObjectResultType,而字符串不是托管对象.

所以我想知道:这是一个核心数据错误吗?或者我的数组是否包含一个指针,该指针以前指向一个ABCVideo已解除分配的实例,并且该堆上的位置随后被__NSCFString实例占用?如果是后者,那怎么会发生这种情况呢?我正在使用ARC,所以很难理解其中一个视频是如何被取消分配的.

kei*_*yip 5

有一个内存管理错误中-[NSFetchedResultsController fetchedObjects]NSFastEnumeration。对象0x136f24480是ABCVideo,但已释放。该内存用于存储__NSCFString。将消息发送到错误的对象EXC_BAD_ACCESS和SIGABRT是常见的结果。

此错误也存在于我的应用程序中,但是即使一次也无法重现。如果您愿意分享一个可以重现该问题的示例项目,我们可以一起解决。

有多种解决方法。关键是要避免NSFastEnumerationfetchedObjects

// 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
Run Code Online (Sandbox Code Playgroud)

如果不幸的是,有人在读这篇文章时使用的是Swift,则即使调用也会导致崩溃,fetchedObjects因为Swift转换NSArrayArrayusing NSFastEnumeration

  • @ChrisStillwell 我不确定我明白你来自哪里。作为问题的作者,如果这是真的,我会认为这是对我问题的回答。(为什么数组不是同构的?因为指向已释放对象的虚假指针巧合地指向具有其他类型的事物)。 (2认同)
  • 在 iOS 14.7 中,使用 fetchedObjects 甚至使用 object(at:) 时,我仍然会遇到随机 EXC_BAD_ACCESS 崩溃的情况。奇怪的是,在调试器崩溃后,我可以毫无问题地访问该行的对象:这个问题似乎最终在 iOS beta 5 中得到了解决。 (2认同)