核心数据:NSObjectID 和 NSTemporaryObjectID 泄漏
Core Data: NSObjectID and NSTemporaryObjectID leaks
在我将我的应用程序发送到 App Store 之前,我喜欢用仪器检查它是否存在内存泄漏和其他可疑的东西。有一个核心数据问题我似乎无法解决,所以我决定创建一个小的测试应用程序来说明这个问题。
有什么问题?
当我在(子)NSManagedObjectContext 中保存实体时,它会传播到其父 NSManagedObjectContext。在此过程中,Core Data 创建了 _NSObjectID
和 NSTemporaryObjectID
的内部实例。由于某些原因,这些实例被遗留下来,摆脱它们的唯一方法是重置父 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 相关的其他问题,尤其是在使用多个不相关的上下文时)。
在我将我的应用程序发送到 App Store 之前,我喜欢用仪器检查它是否存在内存泄漏和其他可疑的东西。有一个核心数据问题我似乎无法解决,所以我决定创建一个小的测试应用程序来说明这个问题。
有什么问题?
当我在(子)NSManagedObjectContext 中保存实体时,它会传播到其父 NSManagedObjectContext。在此过程中,Core Data 创建了 _NSObjectID
和 NSTemporaryObjectID
的内部实例。由于某些原因,这些实例被遗留下来,摆脱它们的唯一方法是重置父 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 相关的其他问题,尤其是在使用多个不相关的上下文时)。