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模式。
代码语言:javascript复制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
代码语言:javascript复制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已经将UILabel
和UIViewController
装饰过了。
@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环境中。
代码语言:javascript复制@MainActor class ListViewModel: ObservableObject {
...
func load() {
loader.loadItems { [weak self] result in
self?.result = result
}
}
}