Before I send my app to the App Store I like to check it for memory leaks and other fishy stuff with instruments. There is one Core Data issue that I can't seem to solve, so I've decided to create a small test app to illustrate the problem.
What's the problem?
When I save an entity in a (child) NSManagedObjectContext it is propagated to its parent NSManagedObjectContext. During this process Core Data creates internal instances of _NSObjectID
and NSTemporaryObjectID
. For some reason these instances are left behind and the only way to get rid of them is to reset the parent NSManagedObjectContext.
My app is of course a lot more complex than this little test app and resetting the NSManagedObjectContext isn't an option for me.
Test app
The test app is a standard iOS app based on the single view template with the CoreData option checked. I've used objective-c to keep it similar to my production app.
- (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;
}
When you run the code above with instruments and the allocations instrument you can see that Core Data leaves some stuff behind.
You can find the full project here: https://github.com/Zyphrax/CoreDataLeak
Things I've tried
I've tried things like [context refreshObject:... mergeChanges:YES]
, adding @autoreleasepool
and/or [context processPendingChanges]
inside the blocks, it all doesn't help. The only way to get it clean is to do a [context reset]
(sledgehammer approach).
It's hard to find other people reporting this problem.
This blog post seems similar:
http://finalize.com/2013/01/04/core-data-issues-with-memory-allocation/
I hope you guys can help me with this.
Here is what I see, which is very similar to yours...
However, I don't know that I would be concerned, unless you see lots of these, and they never go away. I assume the internals of Core Data (including the row cache has) some sort of object caching going on.
On the other hand, my Core Data usage has changed a bit over the past year or two.
Unless it is a very simple app, I almost never create new objects in a child context. I will fetch and modify them, but if I end up creating a new object, I make sure that is done in a sibling context.
However, if you modify your code slightly, by adding this line (with your appropriate error handling - it returns BOOL
) before the initial save...
NSArray *inserted = self.childContext.insertedObjects.allObjects;
[self.childContext obtainPermanentIDsForObjects:inserted error:&error];
you should get something like this instruments report, which shows all objects created as being transient...
Thus, I don't necessarily think it is a permanent leak, because once I force the context to convert to a permanent ID, the objects go away. However, who knows how long they keep those object ID objects cached.
In general, when I create objects in a context that contains a hierarchy, I will always obtain permanent IDs first (for many reasons). However, as I said earlier, I usually create new objects in a context that is directly created to the persistent store (because I have had to deal with other issues related to hierarchies temporary object IDs, especially when using multiple non related contexts).