2015-10-29 30 views
0

我發現在同一個線程和視圖控制器中進度條更新有如此之多的解決方案,但它們似乎與我的例子不一樣。主視圖控制器之外的多線程進度條併發問題

在我的應用程序中,主視圖控制器調用loadIntoCoreData()(在類MyLoadingService中實現),它通過另一個線程將數據異步加載到核心數據中。此功能必須持續更新加載百分比(寫入NSUserDefaults.standardUserDefaults())到主線程,以便它可以顯示在主視圖控制器的進度條上。我曾經用在MainViewController while循環,不斷獲取當前的百分比值,象下面這樣:

class MainViewController { 

    override func viewDidLoad() { 
     MyLoadingService.loadIntoCoreData() { result in 

      NSUserDefaults.standardUserDefaults().setBool(false, forKey: "isLoading") 
      // do something to update the view 

     } 
     self.performSelectorInBackground("updateLoadingProgress", withObject: nil) 
    } 

    func updatingLoadingProgress() { 
     let prefs = NSUserDefaults.standardUserDefaults() 
     prefs.setBool(true, forKey: "isLoading") 

     // here I use a while loop to listen to the progress value 
     while(prefs.boolForKey("isLoading")) { 

      // update progress bar on main thread 
      self.performSelectorOnMainThread("showLoadingProcess", withObject: nil, waitUntilDone: true) 
     } 
     prefs.setValue(Float(0), forKey: "loadingProcess") 
    }  

    func showLoadingProcess() { 
     let prefs = NSUserDefaults.standardUserDefaults() 
     if let percentage = prefs.valueForKey("loadingProcess") { 
      self.progressView.setProgress(percentage.floatValue, animated: true) 
     } 
    } 
} 

而且在類的功能loadIntoCoreData

class MyLoadingService { 

    let context = (UIApplication.sharedApplication()delegate as! AppDelegate).managedObjectContext! 

    func loadIntoCoreData(source: [MyModel]) { 
     var counter = 0 
     for s in source { 
      //load into core data using the class context 

      NSOperationQueue.mainQueue.addOperationWithBlock({ 
       // updating the value of "loadingProcess" in NSUserDefaults.standardUserDefaults() 
       // and synchronize it on main queue 
      }) 
      counter++ 
     } 
    } 
} 

上面的代碼能夠成功運行進度但是由於核心數據上下文的衝突(認爲managedObjectContext似乎沒有被主線程觸及),它經常遇到BAD_ACCESS或其他一些異常(如「無法更新從未插入的對象」)。因此,我不考慮在主線程中使用while循環,而是在每次輸入後使用NSOperationQueue.performSelectorOnMainThread來確認主線程。因此,我將視圖控制器作爲參數sender轉換爲loadCoreData,並調用performSelectorOnMainThread("updateProgressBar", withObject: sender, waitUntilDone: true),但因錯誤「無法識別的選擇器發送給類'XXXXXXXX'」而失敗。所以我想問問是否有可能更新線程之間的UI對象?或者,如何修改我以前的解決方案,以便解決核心數據上下文衝突?任何解決方案,讚賞。

class MyLoadingService { 
    func loadIntoCoreData(sender: MainViewController, source: [MyModel]) { 
     var counter = 0 
     for s in source { 
      //load into core data using the class context 

      NSOperationQueue.mainQueue.addOperationWithBlock({ 
       // updating the value of "loadingProcess" in NSUserDefaults.standardUserDefaults() 
       // and synchronize it on main queue 
      }) 
      NSOperationQueue.performSelectorOnMainThread("updateProgressBar", withObject: sender, waitUntilDone: true) 
      counter++ 
     } 
    } 
    func updateProgressBar(sender: MainViewController) { 
     sender.progressView.setProgress(percentage, animated: true) 
    } 
} 

class MainViewController { 
    override func viewDidLoad() { 
     MyLoadingService.loadIntoCoreData(self) { result in 

      // do something to update the view 
     } 
    } 
} 

回答

0

首先,您以可怕的方式濫用NSUserDefaults。該文檔將其描述爲...

NSUserDefaults類爲 提供了與默認系統交互的編程接口。默認系統允許 應用程序定製其行爲以匹配用戶的偏好。例如,您可以允許用戶確定應用程序顯示的測量單位爲 ,或自動保存文檔 的頻率。應用程序通過將 值分配給用戶默認數據庫中的一組參數來記錄此偏好。 參數被稱爲默認值,因爲它們通常用於 確定應用程序在啓動時的默認狀態或缺省情況下其作用 的方式。

您正在使用它來存儲全局變量。

此外,您完全濫用循環中用戶的CPU,您不斷檢查用戶默認值中的值,並將選擇器剪切到主線程。 「濫用CPU」甚至無法描述此代碼正在做什麼。

您應該使用NSProgress報告進度。有專門用於NSProgressWWDC 2015 presentation


關於您的核心數據使用情況。

不幸的是,由於您有意編輯了所有核心數據代碼,因此不可能說出錯。

但是,基於我所看到的,您可能試圖從您的應用程序委託(可能仍使用過時的限制策略創建)中使用來自後臺線程的託管對象上下文,這是後臺線程就核心數據而言,這是最高的順序。

如果要將數據作爲長時間運行的操作導入,請使用私有上下文,並在後臺執行操作。使用NSProgress將進度傳達給任何想聽的人。


編輯

感謝我的核心數據上下文用法的建議。我深入瞭解了代碼中的所有上下文,並重新組織了上下文, 衝突問題不再發生。至於NSProgress,這是一個 可惜的是WWDC的演講重點在iOS 9上的功能(而我的應用程序的 必須緊湊在iOS 8設備上)。但是,即使我使用NSProgress,我仍然應該告訴主線程有多少數據核心 數據(在另一個線程上)已經有,對吧? NSProgress的 上的線程如何知道我的核心數據線程上的加載進度? - whitney13625

您仍然可以使用NSProgress爲iOS8上,那麼唯一的區別是,你不能明確添加的孩子,但含蓄的方式仍然有效,並且視頻解釋它。

你真的應該看整個視頻,並忘記iOS9的一部分,除了知道你必須隱式添加孩子而不是顯式。

另外,this pre-iOS9 blog後應清除任何關於它的問題。

+0

感謝您對我的核心數據上下文使用的建議。我深入瞭解代碼中的所有上下文,並重新組織內部的上下文,衝突問題不再發生。 至於'NSProgress',可惜的是WWDC的演講重點放在了iOS 9的功能上(而我的應用必須在iOS 8設備上緊湊)。但是,即使我使用'NSProgress',我仍然應該告訴主線程核心數據(在另一個線程上)已經有多少數據,對吧? 'NSProgress'上的線程如何知道我的核心數據線程上的加載進度? – whitney13625