How to init ManagedObjectContext on the right queue?

这一生的挚爱 提交于 2019-12-25 09:26:24

问题


Extremely need an advice, currently run out of ideas. I stack with core data concurrency related issue, to debug I use -"com.apple.CoreData.ConcurrencyDebug" and what I have:

Stack:

Thread 3 Queue: coredata(serial)

0 +[NSManagedObjectContext Multithreading_Violation_AllThatIsLeftToUsIsHonor]: CoreData`-[NSManagedObjectContext executeFetchRequest:error:]:

1 -[NSManagedObjectContext executeFetchRequest:error:]:

2 NSManagedObjectContext.fetch (__ObjC.NSFetchRequest) throws -> Swift.Array:

3 AppDelegate.(fetchRequest NSFetchRequest) -> [A]).(closure #1)

I get into AppDelegate::fetchRequest from here:

let messageRequest: NSFetchRequest<ZMessage> = ZMessage.fetchRequest();
messageRequest.sortDescriptors = [NSSortDescriptor(key: "id", ascending: false)];
let messageArray: Array<ZMessage> = self.fetchRequest(messageRequest);

I perform all coredata stuff on the serial queue(self.queueContainer).

public func fetchRequest<T>(_ request: NSFetchRequest<T>) -> Array<T>
{
    var retval: Array<T> = Array<T>();      
    self.queueContainer.sync {
        do {
            retval = try self.persistentContainer.viewContext.fetch(request);
        } catch {
            let nserror = error as NSError;
            fatalError("[CoreData] Unresolved fetch error \(nserror), \(nserror.userInfo)");
        }
    }
    return retval;
}

This is what I found useful.

Below are some of the rules that must be followed if you do not want your app that uses CoreData to crash (or) corrupt the database:

A NSManagedObjectContext should be used only on the queue that is associated with it.

If initialized with .PrivateQueueConcurrencyType, a private, internal queue is created that is associated with the object. This queue can be accessed by instance methods .performBlockAndWait (for sync ops) and .performBlock (for async ops)

If initialized with .MainQueueConcurrencyType, the object can be used only on the main queue. The same instance methods ( performBlock and performBlockAndQueue) can be used here as well. An NSManagedObject should not be used outside the thread in which it is initialized

Now I'm digging around but honestly speaking can't be sure that my managed object context(MOC) is associated with the right queue.

From manual:

...A consequence of this is that a context assumes the default owner is the thread or queue that allocated it—this is determined by the thread that calls its init method.

In the AppDelegate I do not operate with MOC directly, instead of this I instantiating NSPersistentContainer which owns this MOC. Just in case I'm doing this on the same serial queue as well.

public lazy var persistentContainer: NSPersistentContainer =
{
    self.queueContainer.sync {
        let container = NSPersistentContainer(name: "Joker")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })

        return container
    }
}()

Thanks in advance.


回答1:


I am not a Swift coder , but What is queueContainer?

You should not do the threading yourself, you should use the NSManagedObjectContext block methods as you wrote in your quotation:

The same instance methods ( performBlock and performBlockAndQueue) can be used here as well.

managedObjectContext.performBlock {

Whatever managedObjectContext you are using, you should use that context block method and do your stuff inside the block methods.

Look at the documentation here for examples on how to do this correctly.

Also to avoid crashes and threading errors:

NSManagedObject instances are not intended to be passed between queues. Doing so can result in corruption of the data and termination of the application. When it is necessary to hand off a managed object reference from one queue to another, it must be done through NSManagedObjectID instances.

You retrieve the managed object ID of a managed object by calling the objectID method on the NSManagedObject instance.



来源:https://stackoverflow.com/questions/41988409/how-to-init-managedobjectcontext-on-the-right-queue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!