I discovered a memory leak today that manifested itself when calling executeFetchRequest from my main NSManagedObjectContext. I finally discovered that the known offender was from having my NSManagedObjectContext having it’s parent context be assigned to a private managed object context.
Commenting out the line of code that has my main context assign a private parent class and instead directly be pointed to the NSPersistentStoreCoordinator frees my app of all memory leaks.
I was going off of the following article: http://martiancraft.com/blog/2015/03/core-data-stack/ for a design pattern of how to implement CoreData inside my app. I really like the idea of having a private queue be dedicated to just writing disk, and having the main context as the single source for calling when working with the UI.
So my question is, has anybody else run into this problem, and if so do you know a fix instead of just working on one context only to avoid a memory leak?
Below is the section of my CoreDataStack that shows the two context vars.
private lazy var privateManagedObjectContext: NSManagedObjectContext = {
let moc = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
moc.persistentStoreCoordinator = self.persistentStoreCoordinator
return moc
}()
lazy var managedObjectContext: NSManagedObjectContext = {
let managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
// Commenting out #1 and unCommening #2 prevents the leak from happening
// However when the reverse happens and we create the private context, a memory leak occurs first thing in the app.
// #1
// managedObjectContext.parentContext = self.privateManagedObjectContext
// And instead replace it with this line
// #2
managedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator
return managedObjectContext
}()
Here is what I have for my save method using the main and private managed object context.
func save() {
guard managedObjectContext.hasChanges || privateManagedObjectContext.hasChanges else {
return
}
print("Going to save now")
managedObjectContext.performBlockAndWait() {
do {
try self.managedObjectContext.save()
} catch {
fatalError("Error saving main managed object context! \(error)")
}
}
privateManagedObjectContext.performBlock() {
do {
try self.privateManagedObjectContext.save()
} catch {
fatalError("Error saving private managed object context! \(error)")
}
}
}