【问题标题】:Core Data: NSObjectID and NSTemporaryObjectID leaks核心数据:NSObjectID 和 NSTemporaryObjectID 泄露
【发布时间】:2015-08-04 09:35:13
【问题描述】:

在我将我的应用程序发送到 App Store 之前,我喜欢用仪器检查它是否存在内存泄漏和其他可疑的东西。有一个我似乎无法解决的 Core Data 问题,所以我决定创建一个小型测试应用程序来说明这个问题。

有什么问题?

当我将实体保存在(子)NSManagedObjectContext 中时,它会传播到其父 NSManagedObjectContext。在这个过程中,Core Data 创建了_NSObjectIDNSTemporaryObjectID 的内部实例。出于某种原因,这些实例被遗忘了,摆脱它们的唯一方法是重置父 NSManagedObjectContext。

我的应用程序当然比这个小测试应用程序复杂得多,重置 NSManagedObjectContext 对我来说不是一个选项。

测试应用

测试应用是标准 iOS 应用,基于选中了 CoreData 选项的单一视图模板。我使用了 Objective-c 来保持它与我的生产应用程序相似。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Initialize the Core Data stack
    self.persistentStoreCoordinator = [self persistentStoreCoordinator];

    // Create the a private context
    self.rootContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    self.rootContext.persistentStoreCoordinator = self.persistentStoreCoordinator;

    // Create a child context
    self.childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    self.childContext.parentContext = self.rootContext;

    // Create a person
    [self.childContext performBlockAndWait:^{
        Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.childContext];
        person.name = @"John Smith";
        person.age = 30;

        // Save the person
        [self.childContext save:nil];

        // Save the root context
        [self.rootContext performBlockAndWait:^{
            [self.rootContext save:nil];
        }];
    }];

    return YES;
}

当您使用工具和分配工具运行上面的代码时,您会看到 Core Data 留下了一些东西。

你可以在这里找到完整的项目:https://github.com/Zyphrax/CoreDataLeak

我尝试过的事情

我尝试过 [context refreshObject:... mergeChanges:YES]、在块内添加 @autoreleasepool 和/或 [context processPendingChanges] 之类的方法,但都无济于事。清除它的唯一方法是执行[context reset](大锤方法)。

很难找到其他人报告此问题。 这篇博文看起来很相似:
http://finalize.com/2013/01/04/core-data-issues-with-memory-allocation/

我希望你们能帮助我解决这个问题。

【问题讨论】:

  • 下载的项目,在仪器中运行,它没有看到任何泄漏。 XCode 6.4,部署目标 8.4,iPhone 6 模拟器。如果您可以提供其他内容,我愿意看看。
  • 乔迪感谢您对此进行调查。您应该能够通过以下步骤重现它:(1) 在 Xcode 6.4 中打开项目,(2) 使用 iPad 2 / iOS 8,4 模拟器进行分配/泄漏的配置文件,(3) 泄漏仪器不会检测到任何泄漏,但是当您过滤 NSTemporaryObjectID 或 _NSObjectID 时,您应该会看到应该被释放的持久实例(类似于上面的屏幕截图,对于屏幕截图,我使用了 Xcode 7 分析器)。

标签: core-data memory-leaks instruments nsmanagedobjectcontext


【解决方案1】:

这是我看到的,和你的很相似......

但是,我不知道我会担心,除非你看到很多这样的东西,而且它们永远不会消失。我假设 Core Data 的内部结构(包括行缓存)正在进行某种对象缓存。

另一方面,过去一两年我的 Core Data 使用情况发生了一些变化。

除非它是一个非常简单的应用程序,否则我几乎从不在子上下文中创建新对象。我将获取并修改它们,但如果我最终创建了一个新对象,我会确保在同级上下文中完成。

但是,如果您稍微修改代码,请在初始保存之前添加此行(使用适当的错误处理 - 它返回 BOOL)...

NSArray *inserted = self.childContext.insertedObjects.allObjects;
[self.childContext obtainPermanentIDsForObjects:inserted error:&error];

你应该得到类似这个仪器报告的东西,它显示所有创建的对象都是瞬态的......

因此,我不一定认为这是永久泄漏,因为一旦我强制上下文转换为永久 ID,对象就会消失。但是,谁知道他们将这些对象 ID 对象缓存了多长时间。

一般来说,当我在包含层次结构的上下文中创建对象时,我总是会首先获得永久 ID(出于多种原因)。然而,正如我之前所说,我通常在直接创建到持久存储的上下文中创建新对象(因为我必须处理与层次结构临时对象 ID 相关的其他问题,尤其是在使用多个不相关的上下文时)。

【讨论】:

  • 您好乔迪,感谢您的回答!我将尝试 getPermanentIDsForObject 方法。如果您创建一个直接连接到持久存储的上下文,您如何通知其他上下文您的上下文中所做的更改?
  • Core Data 将在保存上下文时自动将NSManagedObjectContextWillSaveNotificationNSManagedObjectContextDidSaveNotification 发送到默认通知中心。同样,只要对上下文进行更改,就会发送NSManagedObjectContextObjectsDidChangeNotification。很多关于 SO 的问题和很多关于如何做到这一点的信息。如果您发现自己被难住了,请发布另一个问题 - 给我发一条指导我的评论,我会看看。
猜你喜欢
  • 2010-11-24
  • 2011-02-20
  • 1970-01-01
  • 1970-01-01
  • 2011-09-19
  • 2011-01-22
  • 2015-11-05
  • 2011-03-15
  • 1970-01-01
相关资源
最近更新 更多