我發現在同一個線程和視圖控制器中進度條更新有如此之多的解決方案,但它們似乎與我的例子不一樣。主視圖控制器之外的多線程進度條併發問題
在我的應用程序中,主視圖控制器調用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
}
}
}
感謝您對我的核心數據上下文使用的建議。我深入瞭解代碼中的所有上下文,並重新組織內部的上下文,衝突問題不再發生。 至於'NSProgress',可惜的是WWDC的演講重點放在了iOS 9的功能上(而我的應用必須在iOS 8設備上緊湊)。但是,即使我使用'NSProgress',我仍然應該告訴主線程核心數據(在另一個線程上)已經有多少數據,對吧? 'NSProgress'上的線程如何知道我的核心數據線程上的加載進度? – whitney13625