Configure Core Data persistance in Swift 5

I ended up reading more on Core Data, and solving the problem this way:

First, I ended up moving the persistence container into the app delegate. It can be defined as follows:

 import UIKit
 import CoreData

 @UIApplicationMain
 class AppDelegate: UIResponder, UIApplicationDelegate { 

      ... applicationWillTerminate ....

      // add the core data persistance controller
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "mycoolcontainer")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                print("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container;
    }()

    func save() {
        let context = persistentContainer.viewContext;
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                print("Failure to save context: \(error)")
            }
        }
    }

 }

In every view controller that I need to access the context, I'd do something as follows:

import UIKit

class CoolViewController: UIPageViewController, UIPageViewControllerDelegate {

    let appDelegate = UIApplication.shared.delegate as! AppDelegate;

    func storeAndFetchRecords() {
        MySampleClass().doSomething(context: appDelegate.persistentContainer.viewContext);
        appDelegate.save()
    }

}

The classes that need to interact with the context, would do it as follows:

import CoreData

class MySampleClass {

    func doSomething(context: NSManagedObjectContext) {
        for i in 0 ..< 1000 {
            let entry = MyEntity(context: context);
            // do random things
        }
    }
}

Since this lives on the main thread, there is no need to run a "performAndWait". This would only make sense in the event if you initiated a background context, via this syntax:

appDelegate.persistentContainer.newBackgroundContext()

I've decided to add a second answer because the approach is different from my previous answer.

Some further reading on your problem indicates an issue with memory allocation.

In short, my assessment is...

  1. that your iteration for i in 0 ..< 1000 is formally a synchronous process but, due to the call to Core Data within, may be acting "out-of-synch" with Core Data processes. I suspect that whatever Core Data processes are underway within the for...in iteration are physically incapable of completing by the time your next iteration begins and your code is suffering from illegal memory overwrites.

OR

  1. that with 1000 entities, your call to .save creates a massive "hit" to performance and, depending on other processes you have running at the time, especially processes that modify the NSManagedObjectContext, you may simply run out of system resources. I suspect that, depending on "other" processes you have running in your code at the time of the call to .save, you are simply short on system resources and memory overwrites are perhaps a mechanism, albeit failed, as an attempt for iOS to "keep up".

Yeah, I'm covering a lot of ground here but attempting to point you in a direction that will help solve your problem.

Perhaps your question becomes... how do I ensure the Core Data processes within my iteration are complete before stepping and/or saving?

All the books and other information I've read on Core Data suggest that operations within an NSManagedObjectContext are cheap and saves are expensive.

The following is a direct quote from a book, which I suspect is allowable on this site if I credit the authors...

“It’s important to note, though, that inserting new objects into the context or changing existing objects are both very cheap — these operations only touch the managed object context. Inserts and changes all operate within the context without touching the SQLite tier.

Only when these changes are being saved to the store is the cost relatively high. A save operation involves both the coordinator tier and the SQL tier. As such, a call to save() is rather expensive.

The key takeaway is quite simply to reduce the number of saves. But there’s a balance to strike: saving a very large number of changes increases the memory usage because the context has to keep track of these changes until they’re saved. And processing a large change requires more CPU and memory than a smaller one. However, in total, a large changeset is cheaper than many small ones.”

Excerpt From: “Core Data.” by Florian Kugler and Daniel Eggert. (objc.io)

I don't have a clear answer for you now because frankly this will take some time and effort for me to solve, but in the first instance I recommend that you:

  • read up on performance tuning Core Data;
  • investigate stripping back your call to MyEntity(context:) to the bare essentials - for example, creating an entity with relationship faults or creating an entity and calling into memory only those attributes needed for your code to function;
  • investigate the use of NSOperation or Operation to queue your tasks;
  • watch the WWDC2015 talk on "Advanced NSOperations".

Further SO reading, if you're interested:

  • Accepted answer to "iOS error : Heap corruption detected, free list is damaged and Incorrect guard value: 0";
  • "How to 'set a breakpoint in malloc_error_break to debug'"
  • "How do you enable Clang Address Sanitizer in Xcode?"
  • "Crash when updating Dictionary (Heap corruption detected, free list is damaged at …)"