0
点赞
收藏
分享

微信扫一扫

iOS 为啥要回到主线程刷新UI

iOS 为啥要回到主线程刷新UI

在 iOS 的应用开发中,我们常常会遇到需要在主线程中更新用户界面的情况。这是因为 iOS 的 UI 更新是单线程的,也就是只能在主线程上进行。本文将解释为什么要在主线程中更新 UI,介绍相关机制,并通过代码示例来说明。

主线程与 UI 更新

在 iOS 设备中,用户界面的绘制和交互操作都是在主线程上进行的。当用户与应用程序进行交互时(例如触摸屏幕、滑动等),所有与 UI 相关的操作都必须在主线程中执行。如果在非主线程中更新 UI,可能会导致应用崩溃或出现不可预知的结果。

为什么要回到主线程

  1. 性能:主线程用于处理所有 UI 更新和事件循环。如果 UI 更新任务在主线程之外处理,可能会导致 UI 闪烁、响应缓慢甚至卡顿。

  2. 线程安全:UIKit 不是线程安全的,这意味着如果你在多个线程中同时对 UI 进行操作,可能会导致数据竞争和不可预测的行为。

  3. 事件响应:主线程负责处理用户事件。一旦这些事件的响应需要更新 UI,就必须在主线程上完成。

主线程与后台线程的工作流程

为了更好地理解为何需要回到主线程,我们使用序列图来描述主线程与后台线程的交互流程。

sequenceDiagram
    participant User
    participant MainThread as Main Thread
    participant BackgroundThread as Background Thread

    User->>MainThread: Tap on UI Element
    MainThread->>BackgroundThread: Perform Heavy Task
    BackgroundThread-->>MainThread: Task Completed
    MainThread->>MainThread: Update UI

如图所示,当用户触摸 UI 元素时,主线程接收到事件并将重任务的处理转发给后台线程。完成后,后台线程将结果传回主线程,而 UI 更新则在主线程完成。

代码示例

现在我们通过一个简单的代码示例来展示如何在 iOS 应用中确保 UI 更新在主线程中进行。

创建一个简单的应用

假设我们有一个简单的应用,其功能是从后台线程获取一些数据并更新用户界面。我们使用 GCD(Grand Central Dispatch)来管理线程。

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var dataLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 在后台线程中获取数据
        DispatchQueue.global().async {
            // 模拟耗时的网络请求
            let data = self.fetchData()
            
            // 回到主线程更新UI
            DispatchQueue.main.async {
                self.dataLabel.text = data
            }
        }
    }
    
    func fetchData() -> String {
        // 模拟网络请求延迟
        Thread.sleep(forTimeInterval: 2)
        return "Data Loaded"
    }
}

在上述代码中,fetchData() 方法模拟了一个网络请求,使用了 Thread.sleep(forTimeInterval:) 来模拟延迟。在执行耗时操作时,我们使用 DispatchQueue.global().async 将其放入后台线程。在数据获取完成后,我们再使用 DispatchQueue.main.async 切换回主线程更新 dataLabel 的内容。

忽略主线程的后果

我们来看一个不当使用的例子,如果在后台线程直接更新 UI,会引发错误。

DispatchQueue.global().async {
    let data = self.fetchData()
    
    // 以下代码错误,不应该在后台线程直接更新UI
    self.dataLabel.text = data
}

这样做可能导致应用崩溃,错误信息大致为 “UIKitや其他部分のメインスレッドから以外のスレッドの更新無効です。” 当你运行这个代码,系统会报告 UI 更新必须在主线程上的错误。

如何确保 UI 更新在主线程

除了 GCD,iOS 还提供了其他方法确保 UI 更新在主线程上,例如 performSelector(onMainThread:with:waitUntilDone:) 等,但 GCD 是当前推荐的方式。

self.performSelector(onMainThread: #selector(updateLabel(_:)), with: data, waitUntilDone: false)

@objc func updateLabel(_ data: String) {
    self.dataLabel.text = data
}

结论

在 iOS 开发中,更新 UI 的时候一定要确保在主线程执行。这不仅仅是出于性能考虑,更是为了保持应用的稳定性和用户体验。使用 GCD 来管理线程,可以方便地在后台线程进行耗时任务,并轻松地切换回主线程更新 UI。希望通过这篇文章,你可以更深入理解“为什么 iOS 要回到主线程刷新 UI”的原因,避免在未来的开发中出现不必要的问题。保持应用的高性能和流畅的用户体验,是我们每个开发者的职责。

举报

相关推荐

0 条评论