Objective-C completion API如何通过async-await实现调用?
UIKit基于Completion的API转Async/Await的实现方式
UIKit中存在不少基于completion handler的经典API,比如UIApplication的open(_:options:completionHandler:),其原定义为:
func open( _ url: URL, options: [UIApplication.OpenExternalURLOptionsKey : Any] = [:], completionHandler completion: (@MainActor @Sendable (Bool) -> Void)? = nil )
随着Swift结构化并发的引入,苹果为这类方法新增了async版本:
func open( _ url: URL, options: [UIApplication.OpenExternalURLOptionsKey : Any] = [:] ) async -> Bool
苹果官方文档说明这类async/await绑定是通过固定算法自动生成的,但具体转换逻辑是怎样的?常见的猜测有两种:一是直接用continuation包装原有调用,二是采用更复杂的特殊实现。
实际实现方式
苹果确实是通过CheckedContinuation(或其变体)来完成这类自动转换的,具体逻辑大致如下:
- 调用async版本方法时,系统会创建
CheckedContinuation实例,挂起当前任务,等待completion handler触发; - 接着调用原有的带completion的API,传入一个闭包作为completion handler——这个闭包会在任务结束时被调用,通过continuation恢复挂起的任务,并将completion的参数(比如示例中的
Bool结果)作为async方法的返回值传递; - 系统还会自动处理细节问题:比如保证continuation只被调用一次(避免任务悬挂)、处理上下文隔离(比如原API标注
@MainActor时,async版本也会遵循主线程要求)、以及Sendable合规性检查。
你可以用手动模拟的代码来对应苹果自动生成的逻辑:
extension UIApplication { func open(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey : Any] = [:]) async -> Bool { await withCheckedContinuation { continuation in self.open(url, options: options) { success in continuation.resume(returning: success) } } } }
这种自动转换能生效,是因为苹果的算法会识别符合特定模式的API:
- 方法的最后一个参数是completion handler,闭包的参数对应async方法的返回值(若闭包有多个参数,async版本会返回元组);
- 方法本身是异步执行的,且completion handler只会被调用一次。
这种实现既利用了Swift结构化并发的特性,又能最大程度复用原有API的底层逻辑,无需对核心功能做大幅改动。
内容的提问来源于stack exchange,提问作者Isaaс Weisberg
相关产品推荐
相关产品推荐

