为整个类或单个函数添加@MainActor的考量及运行时差异
@MainActor作用范围的选择考量与运行时差异
我编写了如下处理定位逻辑的Locator类代码:
/* @MainActor */ class Locator : NSObject, ObservableObject { private let locationManager: CLLocationManager private var authorizationContinuation: CheckedContinuation<CLAuthorizationStatus, Never>? @Published var authorizationStatus: CLAuthorizationStatus @Published var location: CLLocation? @Published var error: Error? override init() { locationManager = CLLocationManager() authorizationStatus = locationManager.authorizationStatus super.init() locationManager.delegate = self } /* @MainActor */ func checkAuthorizationStatus() async -> CLAuthorizationStatus { let status = locationManager.authorizationStatus if status == .notDetermined { return await withCheckedContinuation { continuation in authorizationContinuation = continuation locationManager.requestWhenInUseAuthorization() } } else { authorizationStatus = status <=== 此处会触发警告 return status } } } extension Locator : CLLocationManagerDelegate { func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { authorizationStatus = manager.authorizationStatus authorizationContinuation?.resume(returning: authorizationStatus) } func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { print(error) self.error = error location = nil } }
如果不为整个类或checkAuthorizationStatus()添加@MainActor,会出现如下运行时警告:
Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:))
一、选择类级或函数级@MainActor的考量
- 线程安全覆盖范围:如果类中所有修改
@Published属性的操作(包括代理方法、各类业务方法)都需要在主线程执行,给整个类加@MainActor更彻底,能一次性保证所有属性修改、方法调用默认绑定主线程,避免遗漏某个方法导致的线程问题。比如这个Locator类,代理方法里也在修改@Published属性,类级标注能直接覆盖这些场景。 - 性能与灵活性平衡:如果类中只有少数方法依赖主线程,其他方法可以在后台线程执行(比如纯计算、IO操作),仅给特定函数加
@MainActor更合适,避免不必要的主线程调度开销,让非主线程依赖的逻辑高效运行。 - 代码可读性与维护性:类级标注能让其他开发者一眼明白这个类的所有操作都和主线程绑定,减少认知成本;函数级标注则更精准,能明确指出哪些逻辑必须在主线程执行,适合逻辑拆分清晰的场景。
二、两种方案的运行时差异
在当前代码场景下,两种方案的运行时表现有明显差异:
- 类级@MainActor:
- 所有实例方法(包括代理方法)都会隐式绑定主线程,系统调用代理方法时会自动调度到主线程执行,所有
@Published属性的修改都在主线程,彻底消除警告; - 任何外部调用这个类的方法时,都会自动切换到主线程执行,无需手动处理线程切换。
- 所有实例方法(包括代理方法)都会隐式绑定主线程,系统调用代理方法时会自动调度到主线程执行,所有
- 仅给checkAuthorizationStatus()加@MainActor:
- 只有该函数的执行会在主线程,能解决函数内修改
authorizationStatus的警告,但代理方法的执行线程由系统回调决定(可能是后台线程),修改@Published属性时仍会触发警告; - 若要彻底解决问题,还需额外给代理方法添加
@MainActor标注,或者在代理方法中手动用DispatchQueue.main.async包裹属性修改代码。
- 只有该函数的执行会在主线程,能解决函数内修改
内容的提问来源于stack exchange,提问作者meaning-matters
相关产品推荐
相关产品推荐

