首页

时间和精神的房子
壹只iOS程序员的修行世界,欢迎来访

如果文章对您有所帮助
将是我最大的荣幸

Xcode9 Main Thread Checker

在子程更新 UI 是一种常见的错误。这会引起 UI 更细失败、显示错误、数据损坏、甚至崩溃。
Main Thread Checker Xcode9 加入的一个独立工具,用来检测使用 Swift 和 C 语言在子线程调用 UIKit、AppKit 等必须在主线程使用的 API。

在 App 启动时,Main Thread Checker 动态的替换了所有只能在主线程调用的方法的实现(已知的在后台线程上可以安全使用的方法不会被替换),替换后的方法带有预先检查当前线程的功能,当发现非法调用时,Xcode 就会断在这行调用中。

不像其他代码诊断工具,Main Thread Checker 不用重新编译并且可以在已有的二进制包上使用。你可以在不使用 Xcode 的情况下在一个 macOS app 上运行 Main Thread Checker。例如在一个持续集成系统上通过注入动态库文件 /Applications/Xcode.app/Contents/Developer/usr/lib/libMainThreadChecker.dylib 来运行它。

性能影响

Main Thread Checker 的性能影响是很小的, 仅有 1–2% CPU 占用,增加的启动时间 0.1 秒。
因为对性能影响较小,Main Thread Checker 在你用 Xcode 运行 App 时是默认开启的。

解决方案

长时间运行的任务(如网络请求)通常都在后台执行,并且提供一个 completion handler 来处理完成后事情。 试图在 completion handler 中获取或更新 UI 可能会导致问题。

let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
   if let data = data {      
      self.label.text = "\(data.count) bytes downloaded"
      // Error: label updated on background thread   
   }
}
task.resume()

这种情况下应该使用 GCD 将调用派发到主线程来解决。

let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
   if let data = data {
      DispatchQueue.main.async { // Correct
         self.label.text = "\(data.count) bytes downloaded"
      }
   }
}
task.resume()

参考资料

[Apple] What's New in Xcode 9
[Apple] Code Diagnostics
基于桥的全量方法Hook方案 - 探究苹果主线程检查实现

关注作者

分享本文

目录