Where should NSManagedObjectContext be created?

前端 未结 2 882
天命终不由人
天命终不由人 2021-01-20 02:46

I\'ve recently been learning about Core Data and specifically how to do inserts with a large number of objects. After learning how to do this and solving a memory leak probl

相关标签:
2条回答
  • 2021-01-20 03:05

    Now I don't understand. Are they saying I am using the same managed object context or I should use the same managed object context? If I am using the same one, how is it that I create a new one on each while loop? Or if I should be using just one global context, how do I do it without causing memory leaks?

    Let's look at the first part of your code...

    while (thereAreStillMoreObjectsToAdd) {
        let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
        managedObjectContext.undoManager = nil
    

    Now, since it appears you are keeping your MOC in the App Delegate, it's likely that you are using the template-generated Core Data access code. Even if you are not, it is highly unlikely that your managedObjectContext access method is returning a new MOC each time it is called.

    Your managedObjectContext variable is merely a reference to the MOC that is living in the App Delegate. Thus, each time through the loop, you are merely making a copy of the reference. The object being referenced is the exact same object each time through the loop.

    Thus, I think they are saying that you are not using separate contexts, and I think they are right. Instead, you are using a new reference to the same context each time through the loop.


    Now, your next set of questions have to do with performance. Your other post references some good content. Go back and look at it again.

    What they are saying is that if you want to do a big import, you should create a separate context, specifically for the import (Objective C since I have not yet made time to learn Swift).

    NSManagedObjectContext moc = [[NSManagedObjectContext alloc]
        initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    

    You would then attach that MOC to the Persistent Store Coordinator. Using performBlock you would then, in a separate thread, import your objects.

    The batching concept is correct. You should keep that. However, you should wrap each batch in an auto release pool. I know you can do it in swift... I'm just not sure if this is the exact syntax, but I think it's close...

    autoreleasepool {
        for item in array {
            let newObject = NSEntityDescription.insertNewObjectForEntityForName ...
            newObject.attribute1 = item.whatever
            newObject.attribute2 = item.whoever
            newObject.attribute3 = item.whenever
        }
    }
    

    In pseudo-code, it would all look something like this...

    moc = createNewMOCWithPrivateQueueConcurrencyAndAttachDirectlyToPSC()
    moc.performBlock {
        while(true) {
            autoreleasepool {
                objects = getNextBatchOfObjects()
                if (!objects) { break }
                foreach (obj : objects) {
                    insertObjectIntoMoc(obj, moc)
                }
            }
            moc.save()
            moc.reset()
        }
    }
    

    If someone wants to turn that pseudo-code into swift, it's fine by me.

    The autorelease pool ensures that any objects autoreleased as a result of creating your new objects are released at the end of each batch. Once the objects are released, the MOC should have the only reference to objects in the MOC, and once the save happens, the MOC should be empty.

    The trick is to make sure that all object created as part of the batch (including those representing the imported data and the managed objects themselves) are all created inside the autorelease pool.

    If you do other stuff, like fetching to check for duplicates, or have complex relationships, then it is possible that the MOC may not be entirely empty.

    Thus, you may want to add the swift equivalent of [moc reset] after the save to ensure that the MOC is indeed empty.

    0 讨论(0)
  • 2021-01-20 03:06

    This is a supplemental answer to @JodyHagins' answer. I am providing a Swift implementation of the pseudocode that was provided there.

    let managedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType)
    managedObjectContext.persistentStoreCoordinator = (UIApplication.sharedApplication().delegate as! AppDelegate).persistentStoreCoordinator // or wherever your coordinator is
    
    managedObjectContext.performBlock { // runs asynchronously
    
        while(true) { // loop through each batch of inserts
    
            autoreleasepool {
                let array: Array<MyManagedObject>? = getNextBatchOfObjects()
                if array == nil { break }
                for item in array! {
                    let newEntityObject = NSEntityDescription.insertNewObjectForEntityForName("MyEntity", inManagedObjectContext: managedObjectContext) as! MyManagedObject
                    newObject.attribute1 = item.whatever
                    newObject.attribute2 = item.whoever
                    newObject.attribute3 = item.whenever
                }
            }
    
            // only save once per batch insert
            do {
                try managedObjectContext.save()
            } catch {
                print(error)
            }
    
            managedObjectContext.reset()
        }
    }
    

    These are some more resources that helped me to further understand how the Core Data stack works:

    • Core Data Stack in Swift – Demystified
    • My Core Data Stack
    0 讨论(0)
提交回复
热议问题