user3519594
user3519594

Reputation: 397

Coredata concurrency issues

I have a NSOperation subclass that has its private context, and a singleton data manager class that has a context on main queue. All the UI and crud operation are done by this singleton class and a background fetch from cloud kit is done by the NSOperation subclass. Had few doubts as below.

  1. Following is the code i have in NSoperation subclass.Can below code create deadlock?

    self.localStoreMOC?.performBlockAndWait({ () -> Void in
    //Long process of fetching data from cloud and pushing changes to cloud happens here. 
    
      var error:NSErrorPointer = nil
    
      if self.localStoreMOC!.hasChanges
      {
        do
        {
          try self.localStoreMOC!.save()
        }
        catch let error1 as NSError
        {
          error.memory = error1
        }
    
        if error == nil
        {
          self.localStoreMOC!.parentContext!.performBlockAndWait({
    
            do
            {
              try self.localStoreMOC!.parentContext!.save()
            }
            catch let error1 as NSError
            {
              print("wasSuccessful error1 \(error1)")
    
            }
    
          })
        }
      }
    }
    
  2. If i have a another singleton class using this class NSManagedOBject do i need to pass them though ID ?

Upvotes: 0

Views: 54

Answers (1)

Marcus S. Zarra
Marcus S. Zarra

Reputation: 46728

First, you need to turn on -com.apple.CoreData.ConcurrencyDebug 1 in your run time arguments. That will help insure you are calling everything on the proper thread/queue.

Second, you are doing a lot of forced unwrapping of optionals, that is a very bad habit to be in. Best to unwrap them properly or use the optional unwrapping.

Third, what happens when you pause the debugger? Where is the line of code that it is pausing on and what queues are you on?

Just turning on the concurrency debug will most likely show you your issue.

Item 2

If you are wanting to pass a reference to a NSManagedObject from one context to another then yes, you need to use the NSManagedObjectID as the NSManagedObject is not safe to pass between contexts.

Code Formatting

Was playing with the formatting a bit, the results may be of interest to you:

guard let local = localStoreMOC else { fatalError("Local store is nil") }
guard let parent = local.parentContext else { fatalError("Parent store is nil") }
local.performBlockAndWait {
    //Long process of fetching data from cloud and pushing changes to cloud happens here. 
    if !local.hasChanges { return }
    do {
        try local.save()
        parent.performBlockAndWait {
            do {
                try parent.save()
            } catch {
                print("wasSuccessful error1 \(error)")
            }
        }
    } catch {
        print("Failed to save local: \(error)")
    }
}

This removes the forced unwrapping of optionals and prints both errors out if you get one in either case.

Update

Also, some developers, say that nested performblockandwait like above will cause deadlock.

performBlockAndWait will never cause a deadlock. It is more intelligent than that.

  • If you are going from one queue to another, then you want each queue to block and wait like the code describes.
  • If you were on the right queue and called performBlockAndWait then the call would effectively be a no-op and will NOT deadlock

Upvotes: 1

Related Questions