性能
MainActor 不是把所有东西都标上
界面类型隔离到主线程。解码、解析、入库放在外面,再把结果送回来。
Swift 并发里,最容易做成的“优化”是给整个模块加上 @MainActor。警告没了,重活也回到主线程上。隔离的意义是划清谁必须在主线程,不是让编译器闭嘴。
谁该在主线程
UIViewController、SwiftUI 的 View、任何直接碰 UIKit 的类型,放在 MainActor。网络模型、解码、图片缩放、数据库写入,不要标 MainActor。它们返回 Sendable 的结果,界面再收。
struct Note: Sendable {
var id: String
var text: String
}
func parseNotes(from data: Data) async throws -> [Note] {
try await Task.detached {
try JSONDecoder().decode([Note].self, from: data)
}.value
}
@MainActor
final class NoteListModel {
var notes: [Note] = []
func reload(data: Data) async throws {
notes = try await parseNotes(from: data)
}
}
从 MainActor 调用一个没有隔离的 async 函数,中间那段可以离开主线程。如果那个函数内部又立刻 await MainActor.run 去做解析,隔离等于没做。
不要用锁补隔离
已经有 actor 或 MainActor,就不要再套 NSLock 去保护同一份状态。两套规则叠在一起,死锁更难看。共享可变状态收进一个 actor,外面只拿值。
编译器提示“这个调用要等主线程”时,先问这件工作是不是界面。是,就标对类型。不是,就把工作移出这个类型,而不是把调用方也标成 MainActor。