You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Swift协议提供异步方法与完成回调二选一的实现选项?

方案可行性分析与实现建议

你的初始方案无法实现“二选一且必须实现其一”的需求——当前结构下,Foo协议强制要求实现带completion的render方法,扩展提供的异步render是基于该回调方法的默认实现,开发者只能先实现回调版,无法选择只实现异步版。

要实现“二选一”的强制要求,可采用以下两种编译时安全的方案:

方案一:协议双方法+互依赖默认实现

将两个render方法都声明在协议中,通过扩展为彼此提供默认实现,让开发者只需实现其中一个即可满足协议要求:

public protocol Foo {
    func render(completion: (Bool) -> Void)
    func render() async -> Bool
}

// 实现异步版时,自动生成回调版的默认实现
public extension Foo {
    func render(completion: (Bool) -> Void) {
        Task {
            let result = await render()
            completion(result)
        }
    }
}

// 实现回调版时,自动生成异步版的默认实现
public extension Foo {
    func render() async -> Bool {
        return await withCheckedContinuation { continuation in
            render(completion: { result in
                continuation.resume(returning: result)
            })
        }
    }
}

注意事项

  • 编译器会强制开发者至少实现一个方法(若两个都不实现,调用时会触发无限递归,因此需在框架文档中明确要求必须实现其一);
  • 该方案无需额外类型转换,框架内部可直接调用任意一个render方法。

方案二:拆分协议+编译时约束

将回调版和异步版拆分为两个子协议,主协议继承两者,通过类型约束确保开发者必须实现其中一个:

// 拆分两个子协议分别定义不同的render接口
public protocol FooCallbackRender {
    func render(completion: (Bool) -> Void)
}

public protocol FooAsyncRender {
    func render() async -> Bool
}

// 主协议,要求遵循至少一个子协议
public protocol Foo: FooCallbackRender & FooAsyncRender {}

// 为实现回调版的类型提供异步接口默认实现
public extension Foo where Self: FooCallbackRender {
    func render() async -> Bool {
        await withCheckedContinuation { cont in
            render(completion: cont.resume(returning:))
        }
    }
}

// 为实现异步版的类型提供回调接口默认实现
public extension Foo where Self: FooAsyncRender {
    func render(completion: (Bool) -> Void) {
        Task {
            completion(await render())
        }
    }
}

框架初始化与调用优化

为避免开发者传入空实现的Foo类型,可在初始化时增加运行时检查:

public class MyFramework {
    private let delegate: Foo
    
    public init(delegate: Foo) {
        guard delegate is FooCallbackRender || delegate is FooAsyncRender else {
            fatalError("Delegate must implement either render(completion:) or render() async")
        }
        self.delegate = delegate
    }
    
    public func performRender() async {
        // 框架内部可直接调用任意版本的render方法
        let result = await delegate.render()
        // 处理渲染结果
    }
}

优势

  • 编译时强制开发者实现对应子协议的方法,避免空实现;
  • 框架内部调用无需区分类型,直接使用统一接口。

内容的提问来源于stack exchange,提问作者Tometoyou

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 04:25:26