主视图控制器

时间:2015-10-29 10:02:20

标签: ios multithreading core-data concurrency uiprogressview

我在同一个线程和视图控制器中找到了很多进度条更新的解决方案,但是它们似乎与我的情况不同。

在我的应用程序中,主视图控制器调用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++
        }
    }
}

上面的代码可以成功运行进度条,但是由于核心数据上下文的冲突,它经常会遇到BAD_ACCESS或其他一些异常(例如"无法更新从未插入的对象")似乎managedObjectContext没有被主线程触及)。因此,我考虑使用NSOperationQueue.performSelectorOnMainThread来确认每个条目后的主线程,而不是使用while循环监听主线程。因此,我将我的视图控制器作为参数sender添加到loadCoreData并调用performSelectorOnMainThread("updateProgressBar", withObject: sender, waitUntilDone: true),但失败并显示错误"无法识别的选择器已发送到班级' XXXXXXXX'&# 34 ;.所以我想问一下是否有可能在线程之间更新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
        }
    }
} 

1 个答案:

答案 0 :(得分:0)

首先,你以可怕的方式滥用NSUserDefaults。文档将其描述为......

  

NSUserDefaults类为其提供编程接口   与默认系统交互。默认系统允许   应用程序以自定义其行为以匹配用户的首选项。   例如,您可以允许用户确定哪些单位   测量您的应用程序显示或文档的频率   自动保存。应用程序通过分配记录此类首选项   值为用户默认数据库中的一组参数。该   参数被称为默认值,因为它们是常用的   确定应用程序在启动时的默认状态或其行为方式   默认情况下。

您正在使用它来存储全局变量。

此外,您在循环中完全滥用用户的CPU,您不断检查用户默认值中的值,并将选择器剪切到主线程。 "滥用CPU"甚至没有接近描述这段代码的作用。

您应该使用NSProgress来报告进度。 WWDC 2015 presentation专门用于NSProgress

关于您的核心数据使用情况。

不幸的是,由于您有意修改了所有核心数据代码,因此无法说明出现了什么问题。

但是,基于我所看到的,您可能正在尝试使用来自应用程序委托的托管对象上下文(可能仍然使用已弃用的限制策略创建)来自后台线程,这是最高级别的主要罪行关于核心数据的订单。

如果要将数据导入为长时间运行的操作,请使用私有上下文,并在后台执行操作。使用NSProgress将进度传达给任何想要聆听的人。

修改

  

感谢您对我的核心数据上下文使用情况的建议。我深入研究了一切   我的代码中的上下文并重新组织了内部的上下文   冲突问题不再发生。至于NSProgress,它是一个   遗憾的是WWDC的演示集中在iOS 9的功能上(同时   我的应用程序必须在iOS 8设备上压缩)。但是,即使我使用   NSProgress,我还是应该告诉主线程核心有多少数据   数据(在另一个线程上)已经有了,对吧?如何打开线程   NSProgress知道我的核心数据线程的加载进度? -   whitney13625

你仍然可以使用NSProgress for iOS8,那么唯一真正的区别是你不能明确地添加孩子,但隐式的方式仍然有效,并且该视频也解释了它。

你真的应该观看整个视频并忘记iOS9部分,除非要知道你必须隐式添加子项而不是显式。

此外,this pre-iOS9 blog帖子应该清除您对此有任何疑问。