使用核心数据.mainQueueConcurrencyType时冻结的UI

时间:2018-08-13 02:49:32

标签: ios swift core-data concurrency

问题:使用.mainQueueConcurrencyType线程处理核心数据时,UI被冻结

UI:任何UI updatesUITable scrolling等,但是在这个问题中,我使用SwiftSpinner作为活动指示器的示例, https://github.com/icanzilb/SwiftSpinner

设置:

iOS:11.4,设备:iPhone 7 plus,Xcode:9.4.1

我的代码如下

let bgContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)


func doCoreData(progress : @escaping (Double) -> ()) {
    bgContext.performAndWait {
        for i in 1...10{
            let cent : Double = Double(i)/Double(10)
            // do some coredata work adding and updating
            progress(cent)
            // hide SwiftSpinner at the end
            if i == 10 {
                SwiftSpinner.hide()
            }
        }
    }
}

func orgnizeThings (){
    doCoreData { (cent) in
        DispatchQueue.main.async {
            let perCent = cent * 100
            SwiftSpinner.show(progress: cent, title: "loading \(perCent)")
        }
    }
}

调用orgnizeThings()函数将完成 Core Data 工作,但我想向用户展示该函数完成的程度

在此设置中,SwiftSpinner UIView被冻结

1 个答案:

答案 0 :(得分:1)

核心数据不是线程安全的。如果同时写入多个线程,将并发类型从MainQueueConcurrencyType更改为PrivateQueueConcurrencyType可能导致应用程序崩溃。更好的方法是具有多个具有父子关系的管理对象上下文:

 let mainMOC = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
 let childMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
 childMoc.parentContext = mainMoc

  childMOC.performBlock{
            for i in 1...10{
                let cent : Double = Double(i)/Double(10)
                // do some coredata work adding and updating
                progress(cent)

            }

            do {
                try childMoc.save()
                mainMOC.performBlockAndWait{
                    do {
                        try mainMoc.save()
                        SwiftSpinner.hide()
                    } catch {
                        fatalError("Failure to save context: \(error)")
                    }
                }
            } catch{
                fatalError("Failure to save context: \(error)")
            }

  }

保存子上下文后,更改将转到父上下文。保存父上下文后,更改将转到持久性存储协调器。主线程不会被阻塞,因为所有繁重的写操作都将在PrivateMcurrencyType的childMoc中完成。

如果性能仍然很差,那是因为从MainContext写入光盘是一项昂贵的操作。您可以创建一个将写入光盘的主上下文,而不是直接将父上下文写入光盘,一旦保存了主上下文,主上下文更改将进入Maser上下文。

enter image description here

有关更多详细信息,请阅读以下中篇文章:https://medium.com/soundwave-stories/core-data-cffe22efe716