hiccLoghicc log by wccHipo日志

使用@MainActor自动在主线程更新UI

Swift 5.5 终于为开发者带来了async,await,actor这些便捷的异步语法,而其中一个小小的@MainActor语法,能带来让我们的开发更加便捷安全。

手动dispath到主线程#

在swift 5.5 之前,我们需要手动使用DispatchQueue.main来让代码运行在主线程,特别是UI更新操作。这样没问题,但是略显麻烦,而且容易遗漏。

class ProfileViewController: UIViewController { private let userID: User.ID private let loader: UserLoader private lazy var nameLabel = UILabel() private lazy var biographyLabel = UILabel() ... private func loadUser() { loader.loadUser { [weak self] result in DispatchQueue.main.async { switch result { case .success(let user): self?.nameLabel.text = user.name self?.biographyLabel.text = user.biography case .failure(let error): self?.showError(error) } } } } }

@MainActor#

Swift 5.5 内置了的actor,MainActor被装饰的操作自动运行在主线程。

首先我们需要将我们的异步callback代码,转换成async/await模式。

extension UserLoader { func loadUser() async throws -> User { try await withCheckedThrowingContinuation { continuation in loadUser { result in switch result { case .success(let user): continuation.resume(returning: user) case .failure(let error): continuation.resume(throwing: error) } } } } }

在UIKit中使用@MainActor#

class ProfileViewController: UIViewController { ... private func loadUser() { async { do { let user = try await loader.loadUser() nameLabel.text = user.name biographyLabel.text = user.biography } catch { showError(error) } } } }

等等,没有看到@MainAcotr?

那是因为apple已经将UILabelUIViewController 装饰过了。

@MainActor class UILabel: UIView @MainActor class UIViewController: UIResponder

也就是,在swift 的concurrency 系统中,被@MainActor装饰过的类,及其子类的属性和方法,都会自动在主线程中,get,set,或者call

自定义UI class#

假设,我们SwiftUI中的一个实现ObservableObject的类,其中被@Published装饰的属性需要自动运行在主线程。 我们只需要装饰@MainActor即可。

@MainActor class ListViewModel: ObservableObject { @Published private(set) var result: Result<[Item], Error>? private let loader: ItemLoader ... func load() { async { do { let items = try await loader.loadItems() result = .success(items) } catch { result = .failure(error) } } } }

就是这么简单!!!

@MainActor 只能运行在async/await环境中。#

@MainActor class ListViewModel: ObservableObject { ... func load() { loader.loadItems { [weak self] result in self?.result = result } } }
👉@MainActor 不会对Callback中的代码生效

@MainActor 必须使用在Swift async/await Concurrency环境中,否则无法生效。因为actor必须通过同步方式来获取。

参考自: