我有一个同步来自云的数据的应用程序,这发生在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
}
编辑:
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])
})
}
})
我应该从这里删除performBlockAndWait()
并将其放在我访问本地数据库和托管对象的所有位置吗?
答案 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是安全的,而且确实如此。但如果你足够努力,你仍然可以绊倒它:)
请显示funSyncStart
和syncCompletionBlock
,以便我可以关注您的代码并帮助您清理它。
其实我在上一个问题上错过了你的编辑:)。并发调试变量指出的问题现在被清除了。它还使用了GDCoreDataConcurrencyDebugging.h,它显示对象是在另一个线程上发布的,并通过添加autoreleasepool来解决它。实际上有一个疑问,即使一切都工作正常,如果我错过了一些东西,它只是在用户发生数据丢失之前得到指出。感谢您的帮助,马库斯已经编辑了添加syncCompletionBlock的问题,我们只是调用另一种方法的funstartsync等等。
首先,我不相信第三方库对Core Data进行并发性测试。 -com.apple.CoreData.ConcurrencyDebug 1
是唯一的并发规范测试。那个吊舱正在做什么是非常可疑的。
其次,您不需要自动释放池;一点都不在某些情况下,特别是当您使用这样的队列时,对象将在稍后被系统释放。您提供的代码不会泄漏内存。
第三,你的问题是如果某些东西会死锁,这意味着你没有测试这段代码。它陷入僵局吗?你运行代码了吗?
如果确实是(而不是戏剧性的问题)锁定,那么当它锁定时,进入调试器,暂停应用程序并查看它在哪里死锁。
说完了所有这些。假设问题中的第一个代码块是funSyncStart
的内部,那么您在同一个上下文中连续两次调用performBlockAndWait
。虽然不应该导致死锁,但它是浪费和糟糕的代码。如果你在main方法中调用performBlockAndWait
,那么没有理由在你从main调用的任何子方法中再次调用它!您已经在正确的队列中,没有理由再次跳到正确的队列。
performBlockAndWait
跳转到正确的队列,执行闭包并阻塞调用队列。如果你继续打电话,它应该是一个无操作,但理想情况下你应该只调用一次以保持代码清洁。