为何非async方法调用在async函数内需添加await修饰?
问题:async方法里调用
NotificationCenter.addObserver为啥被要求加await? 我之前一直这么写通知监听:
NotificationCenter.default.addObserver(self, selector: #selector(foo), name: UIApplication.didEnterBackgroundNotification, object: nil)
但把这段代码移到async方法里时,编译器突然要求给这行加await修饰——明明addObserver根本不是异步方法:
func setup() async { let importantStuff = await someTask() // 存储重要数据,初始化完成 // 这行不加await就编译不过 // 但addObserver根本不是async方法啊🤨 await NotificationCenter.default.addObserver(self, selector: #selector(foo), name: UIApplication.didEnterBackgroundNotification, object: nil) // 但这么写就完全没问题 listen() } func listen() { NotificationCenter.default.addObserver(self, selector: #selector(foo), name: UIApplication.didEnterBackgroundNotification, object: nil) }
最简复现示例
下面的代码编译失败(编译器强制要求给addObserver调用加await):
class Test { func thisFailsToCompile() async { let stuff = try! await URLSession.shared.data(from: URL(string: "https://google.com")!) NotificationCenter.default.addObserver(self, selector: #selector(foo), name: UIApplication.didEnterBackgroundNotification, object: nil) } @objc func foo() { } }
原因分析
这是Swift编译器的上下文推断bug:当你在async方法里执行过await操作(比如示例里的URLSession.data(from:)),编译器的状态机切换到“异步上下文”后,会错误地把后续所有同步方法调用判定为需要await,哪怕这个方法本身是同步的。
解决办法
几种简单的绕开方式:
- 把
addObserver调用放到单独的同步方法里(就像示例里的listen()),再在async方法里调用这个同步方法 - 给
NotificationCenter.default加显式类型标注,帮编译器纠正上下文判断:let nc: NotificationCenter = .default nc.addObserver(self, selector: #selector(foo), name: UIApplication.didEnterBackgroundNotification, object: nil)
这个bug在Swift的后续版本中已经被修复,升级到较新的Swift版本后,问题会自动消失。
内容的提问来源于stack exchange,提问作者Peter Parker
相关产品推荐
相关产品推荐

