【问题标题】:frozen UI when working with core data .mainQueueConcurrencyType使用核心数据时冻结的 UI .mainQueueConcurrencyType
【发布时间】:2018-08-13 02:49:32
【问题描述】:

问题:使用 .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 被冻结

【问题讨论】:

  • “一些核心数据的工作添加和更新”听起来很重要。你可能不想在主队列上这样做,因为这样做会阻塞你的 UI,直到它完成

标签: ios swift core-data concurrency


【解决方案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)")
            }

  }

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

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

欲了解更多详情,请阅读这篇中型帖子:https://medium.com/soundwave-stories/core-data-cffe22efe716

【讨论】:

  • 完全不改变 mainContext 中的任何内容你怎么看?
  • 如果 MainContext 正在写入持久存储,同时假设用户正在尝试滚动,则会出现性能问题。我认为使用 Worker Context 和 Master Context 来获得流畅的用户体验是很有意义的,因此应该避免更改主上下文中的任何内容。
  • 这里的主要上下文不是瓶颈吗?我知道保存到父上下文可能不像保存到磁盘那样昂贵,但是当您尝试从主上下文读取同时保存到主上下文时,您仍然可以阻止 UI。让 main 和 worker 上下文都是 master 的孩子不是更好吗?然后可以严格地只读主要上下文。您必须订阅保存通知并管理合并,但可以避免 UI 性能问题。
猜你喜欢
  • 1970-01-01
  • 2011-12-10
  • 1970-01-01
  • 2016-03-20
  • 1970-01-01
  • 2019-10-08
  • 2011-07-25
  • 1970-01-01
  • 2013-11-27
相关资源
最近更新 更多