核心数据:NSObjectID 和 NSTemporaryObjectID 泄漏

Core Data: NSObjectID and NSTemporaryObjectID leaks

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

有什么问题?

当我在(子)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 and/or [context processPendingChanges],但都无济于事。让它干净的唯一方法是做一个[context reset](大锤方法)。

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

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

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

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

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

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

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

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

您应该得到类似于此仪器报告的内容,其中显示创建的所有对象都是瞬态的...

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

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