【问题标题】:why is NSFetchedResultsController's fetchedObjects array not always homogeneous为什么 NSFetchedResultsController 的 fetchedObjects 数组并不总是同质的
【发布时间】:2016-05-02 01:05:27
【问题描述】:

为了不埋没问题,我将打开我的核心问题:为什么我的 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];

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

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

但我在我们的 HockeyApp 崩溃报告中注意到,当我的通知处理程序试图从 fetchedObjects 获取 filteredArrayUsingPredicate 时,我收到了一个 SIGABRT,这种情况非常罕见:

*** 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 实例,而堆上的该位置随后被 __NSCFString 实例占用?如果是后者,那怎么会这样呢?我正在使用 ARC,因此很难理解其中一个视频是如何被释放的。

【问题讨论】:

  • 我怀疑存在核心数据错误。 iOS9 引入了几个 bug,尤其是 NSFetchedResultsControllers,这可能是另一个。
  • 数组本身应该保留它的对象,所以除非你以某种方式releaseing one-too-many,否则它们不应该被释放。此外,对于 ARC,几乎可以肯定情况并非如此。可能是 ARC 错误或核心数据错误。你能排除这与NSFetchedResultsController 的缓存有关吗?禁用缓存和重现?
  • 由于您可以重现它,因此检测何时发生这种情况并检查字符串的内容可能很有用。我不知道为什么字符串在那里,但它的内容可能指向一个原因。
  • 很可能有一个指针曾经指向一个 NSManagedObject(子类),现在指向一个被 NSString 对象占用的内存位置。真正的问题是:你在哪里访问这个指针,为什么它不再指向一个 NSManagedObject?您是否正在跨线程使用此数组?你把自己的参考资料保存在某处吗?你能分享更多关于到底发生了什么吗?这对我来说似乎不是核心数据错误。
  • @JanGreve 设置每次复制尝试都需要一段时间,而且它不会每次都触发错误,但我能够设置 9 次尝试(5 次禁用缓存,4 次禁用缓存)启用缓存)。即使没有缓存,我也会崩溃,尽管它更改为 EXC_BAD_ACCESS 而不是上面的 SIGABRT。 (可悲的是,如果我启用僵尸对象,我根本无法重现问题。)

标签: ios objective-c core-data nsfetchedresultscontroller nsfetchrequest


【解决方案1】:

-[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

如果不幸的是,有人在使用 Swift,即使调用 fetchedObjects 也会崩溃,因为 Swift 使用 NSFastEnumerationNSArray 转换为 Array

【讨论】:

  • @ChrisStillwell 我不确定我是否了解您来自哪里。作为问题的作者,如果这是真的,我会认为这是对我的问题的回答。 (为什么数组不是同质的?因为指向释放对象的虚假指针巧合地指向具有其他类型的事物)。
  • 这个 bug 从 iOS 8.x 开始就存在。我不确定是否有人向 Apple 报告了此事。我想至少记者没有任何苹果的示例项目。如果没有提供示例项目,Apple 喜欢假装没有错误。
  • 我仍然会在 iOS 14.7 中使用 fetchedObjects 甚至使用 object(at:) 随机崩溃 EXC_BAD_ACCESS。调试器崩溃后奇怪的事情是我可以毫无问题地访问该行的对象:这个问题似乎最终在 iOS beta 5 中得到修复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多