具有多个上下文和嵌套perfromblock的Coredata并且实现混淆

时间:2016-05-13 06:49:44

标签: ios core-data concurrency deadlock nsoperation

我有一个同步来自云的数据的应用程序,这发生在NSOperationqueue上。该应用程序有一个具有主要并发性的主MOC和一个具有私有队列并发性的子MOC的NSOperation子类。每当用户按下启动NSoperation同步数据的同步按钮时。流程如下:

local.performBlockAndWait 
{
//1. upload local to cloud and resolve conflicts . 

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)")
}



//2. download cloud to local and save changes to local 

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)")
}

}

注意:上述过程发生在nsoperation子类中,该子类在NSOperation队列中进行调度。因此,不会发生变化,它将出现在主要问题上。

此代码是否会导致死锁?谷歌搜索每个人都认为混合意见和句子就像永远不会在mainMOC上使用performblockandwait等。所以我需要更改上面的代码吗?

编辑: 本地商店的创建如下:

@objc public class CKSIncrementalStoreSyncOperation: NSOperation
{

override public func main() 
{
    self.localStoreMOC = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType)
    self.localStoreMOC?.parentContext = OSCDStackManager.sharedManager().managedObjectContext;

 self.localStoreMOC?.performBlockAndWait(
 { () -> Void in

        autoreleasepool 
        {
            if self.syncCompletionBlock != nil
            {
                self.funSyncStart({ (isSycned, error) -> () in

                        self.syncCompletionBlock!(syncError: error)
                 })                
            }

        }
})

  }
//Sync Code 
}

编辑:

  1. SyncompletionBlock:它只是通知完成同步操作并进行其他UIupdates。如下所示。

    self.syncOperation?.syncCompletionBlock =  ({(error) -> Void in
    
        self.isSyncing = false
    
        if error == nil
        {
            NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
    
                AlertControllerAlloction.funReturnAlert("Synced successfully.", subTitleP: "Synced successfully.", closeButtonTitleP: "Ok", durationP: 0.0, type: 5, array: nil, delegate: nil)
    
                let myDict : NSDictionary = ["isSuccess":true];
    
                NSNotificationCenter.defaultCenter().postNotificationName(secureCloudCompletionNotification, object: nil, userInfo: myDict as [NSObject : AnyObject])
            })
    
        }
        else
        {
            NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
    
                AlertControllerAlloction.funReturnAlert("Server Busy", subTitleP: "An error occurred during the synchronization process. Please try again after a few minutes.", closeButtonTitleP: "Ok", durationP: 0.0, type: 2, array: nil, delegate: nil) // text change 10/11
    
                let myDict : NSDictionary = ["isSuccess":false,"SyncError":error!]
    
                NSNotificationCenter.defaultCenter().postNotificationName(secureCloudCompletionNotification, object: nil, userInfo: myDict as [NSObject : AnyObject])
            })
        }
    })
    
    1. funStartSync方法调用另一个方法,该方法又调用另一个方法,依此类推。在整个NSOperation类中使用本地上下文来获取在本地数据库中进行的更改以上载到云,如果发生冲突,则通过比较上次修改日期来解决,并且在第2部分中从云中获取更改后,将这些更改应用于本地数据库中。因此,托管对象遍布整个班级。
  2. 我应该从这里删除performBlockAndWait()并将其放在我访问本地数据库和托管对象的所有位置吗?

1 个答案:

答案 0 :(得分:0)

这是您之前提出的问题。

你的main()方法作为很多额外的工作,它可能不需要。

@objc public class CKSIncrementalStoreSyncOperation: NSOperation {

  override public func main() {
    self.localStoreMOC = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType)
    self.localStoreMOC?.parentContext = OSCDStackManager.sharedManager().managedObjectContext;

    self.localStoreMOC?.performBlockAndWait {
      if self.syncCompletionBlock != nil {
        self.funSyncStart({ (isSycned, error) in
          self.syncCompletionBlock!(syncError: error)
        })                
      }
    }
  }
}

在私有MOC的队列中调用syncCompletionBlock有什么意义?那是你的其他代码块存在的地方吗?如果是这样那么这很可能是你的问题,因为你通过从两个差异函数调用它两次来绊倒performBlockAndWait,从而混淆了上下文。我之前说过,拨打PB& W是安全的,而且确实如此。但如果你足够努力,你仍然可以绊倒它:)

请显示funSyncStartsyncCompletionBlock,以便我可以关注您的代码并帮助您清理它。

  

其实我在上一个问题上错过了你的编辑:)。并发调试变量指出的问题现在被清除了。它还使用了GDCoreDataConcurrencyDebugging.h,它显示对象是在另一个线程上发布的,并通过添加autoreleasepool来解决它。实际上有一个疑问,即使一切都工作正常,如果我错过了一些东西,它只是在用户发生数据丢失之前得到指出。感谢您的帮助,马库斯已经编辑了添加syncCompletionBlock的问题,我们只是调用另一种方法的funstartsync等等。

首先,我不相信第三方库对Core Data进行并发性测试。 -com.apple.CoreData.ConcurrencyDebug 1是唯一的并发规范测试。那个吊舱正在做什么是非常可疑的。

其次,您不需要自动释放池;一点都不在某些情况下,特别是当您使用这样的队列时,对象将在稍后被系统释放。您提供的代码不会泄漏内存。

第三,你的问题是如果某些东西会死锁,这意味着你没有测试这段代码。它陷入僵局吗?你运行代码了吗?

如果确实是(而不是戏剧性的问题)锁定,那么当它锁定时,进入调试器,暂停应用程序并查看它在哪里死锁。

说完了所有这些。假设问题中的第一个代码块是funSyncStart的内部,那么您在同一个上下文中连续两次调用performBlockAndWait。虽然不应该导致死锁,但它是浪费和糟糕的代码。如果你在main方法中调用performBlockAndWait,那么没有理由在你从main调用的任何子方法中再次调用它!您已经在正确的队列中,没有理由再次跳到正确的队列。

performBlockAndWait跳转到正确的队列,执行闭包并阻塞调用队列。如果你继续打电话,它应该是一个无操作,但理想情况下你应该只调用一次以保持代码清洁。