Swift 5.10:非隔离deinit访问非Sendable类型警告的解决办法
问题原因
开启完整严格并发检查后,Swift会强制要求跨隔离上下文访问的类型符合Sendable协议,以此保障线程安全。你的AuthController未实现Sendable,而deinit属于非隔离上下文,直接访问非Sendable类型的属性就会触发警告,Swift 6中这会升级为错误。
以下是几种可行的解决方法:
方法1:让AuthController符合Sendable协议
如果AuthController的实现本身是线程安全的(比如没有共享可变状态,或者所有状态访问都做了线程同步),直接让它遵循Sendable协议即可:
class AuthController: Sendable { func signInAnonymouslyIfNecessary() { } }
如果AuthController内部包含非Sendable的属性,但你能手动保证这些属性的线程安全访问,可以使用@unchecked Sendable(需谨慎,仅当你确认线程安全时使用):
class AuthController: @unchecked Sendable { // 假设这个属性的访问已经做了线程同步 private var mutableState: String = "" func signInAnonymouslyIfNecessary() { } }
方法2:用Task包裹deinit中的调用
如果不想修改AuthController的协议一致性,可以将调用放到异步任务中,让访问在隔离上下文执行:
deinit { Task { authController.signInAnonymouslyIfNecessary() } }
注:Task会异步执行代码,但由于authController被AuthFormNavC强持有,deinit执行时authController尚未释放,因此调用是安全的。如果你的方法需要同步执行,这种方式不适用。
方法3:将导航控制器标记为@MainActor
如果signInAnonymouslyIfNecessary()必须在主线程执行,且AuthController的操作本身是主线程安全的,可以让AuthFormNavC归属于MainActor,这样deinit会在主线程隔离上下文执行,访问authController就不会有警告:
@MainActor class AuthFormNavC: UINavigationController { let authController: AuthController init(authController: AuthController) { self.authController = authController super.init(rootViewController: ConsentVC()) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } deinit { authController.signInAnonymouslyIfNecessary() } }
如果AuthController本身也是@MainActor修饰的,这种方式的线程安全性会更有保障。
内容的提问来源于stack exchange,提问作者Filippo

