如何定义可存储特定闭包签名集合的变量?优化处理器闭包初始化实现
如何定义支持多种闭包签名的变量
嘿,针对你这个需求,在Swift里不用枚举包装闭包也能实现,我给你整理了几个实用且简洁的方案,按需挑选就行:
方案1:自定义类型擦除结构体(灵活通用)
先假设你的六种闭包签名大概是基于Payload的不同入参/返回值组合(从你给出的代码片段推测),先定义好这些闭包类型:
struct Payload { let stringVal: String let intVal: Int } // 示例六种闭包签名,你可以根据实际情况调整 typealias Closure1 = (Payload) -> Void typealias Closure2 = (Payload, String) -> Bool typealias Closure3 = (Payload) -> String typealias Closure4 = (Int, Payload) -> Void typealias Closure5 = () -> Payload typealias Closure6 = (Payload) throws -> Int
然后我们写一个类型擦除的结构体AnyPayloadClosure,把不同签名的闭包统一成同一个类型:
struct AnyPayloadClosure { private let _execute: (Any...) -> Any? init<T>(_ closure: T) { // 匹配每种闭包签名,把调用逻辑封装起来 if let closure = closure as? Closure1 { _execute = { args in guard let payload = args.first as? Payload else { return nil } closure(payload) return nil } } else if let closure = closure as? Closure2 { _execute = { args in guard let payload = args[0] as? Payload, let str = args[1] as? String else { return nil } return closure(payload, str) } } else if let closure = closure as? Closure3 { _execute = { args in guard let payload = args.first as? Payload else { return nil } return closure(payload) } } // 把剩下的三种闭包类型也按这个逻辑补充进去 else { fatalError("当前不支持该闭包类型,请补充处理逻辑") } } // 为每种闭包签名提供对应的调用方法,避免用Any...的不确定性 func call(with payload: Payload) { _execute([payload]) } func call(with payload: Payload, appendStr: String) -> Bool? { return _execute([payload, appendStr]) as? Bool } // 对应其他闭包签名的调用方法也要补充哦 }
接下来处理器的初始化就很简单了,甚至可以写便捷初始化器直接接收各种闭包:
struct Processor { let closure: AnyPayloadClosure // 基础初始化器 init(closure: AnyPayloadClosure) { self.closure = closure } // 便捷初始化器,直接接收Closure1类型 init(_ closure: @escaping Closure1) { self.closure = AnyPayloadClosure(closure) } // 便捷初始化器,直接接收Closure2类型 init(_ closure: @escaping Closure2) { self.closure = AnyPayloadClosure(closure) } // 其他闭包类型的便捷初始化器依次补充 }
使用的时候完全不用手动包装,直接传闭包就行:
// 直接传Closure1类型的闭包 let processor1 = Processor { payload in print("收到字符串:\(payload.stringVal)") } // 直接传Closure2类型的闭包 let processor2 = Processor { payload, str in return payload.intVal > Int(str) ?? 0 }
方案2:基于协议的类型擦除(类型安全优先)
如果你想更严格地约束闭包的类型,避免Any带来的不确定性,可以用协议+类型擦除的方式:
// 定义协议,约束闭包的输入输出 protocol PayloadClosure { associatedtype Input associatedtype Output func execute(with input: Input) -> Output } // 为每种闭包签名实现协议 struct ConcreteClosure1: PayloadClosure { typealias Input = Payload typealias Output = Void private let closure: Closure1 init(_ closure: @escaping Closure1) { self.closure = closure } func execute(with input: Payload) { closure(input) } } struct ConcreteClosure2: PayloadClosure { typealias Input = (Payload, String) typealias Output = Bool private let closure: Closure2 init(_ closure: @escaping Closure2) { self.closure = closure } func execute(with input: (Payload, String)) -> Bool { closure(input.0, input.1) } } // 类型擦除的包装结构体 struct AnyPayloadClosure<Input, Output>: PayloadClosure { private let _execute: (Input) -> Output init<C: PayloadClosure>(_ closure: C) where C.Input == Input, C.Output == Output { _execute = closure.execute } func execute(with input: Input) -> Output { _execute(input) } }
这种方式的好处是类型完全安全,调用时不会有类型转换的风险,但需要为每种闭包签名写对应的协议实现,样板代码会多一点。
方案3:初始化器重载+内部存储(最简洁)
如果你的处理器只需要在内部调用闭包,不需要把这个多类型闭包变量对外暴露,那直接用初始化器重载是最省事的:
struct Processor { private let closure: Any private let callHandler: (Any...) -> Any? // 针对Closure1的初始化器 init(_ closure: @escaping Closure1) { self.closure = closure self.callHandler = { args in guard let payload = args.first as? Payload else { return nil } closure(payload) return nil } } // 针对Closure2的初始化器 init(_ closure: @escaping Closure2) { self.closure = closure self.callHandler = { args in guard let payload = args[0] as? Payload, let str = args[1] as? String else { return nil } return closure(payload, str) } } // 其他闭包类型的初始化器依次补充 // 对外提供调用方法 func process(_ payload: Payload) { _ = callHandler([payload]) } func process(_ payload: Payload, checkStr: String) -> Bool? { return callHandler([payload, checkStr]) as? Bool } }
这个方案完全不需要额外定义任何包装类型,用户使用时直接传闭包,和普通闭包参数没区别,非常直观。
方案选择建议
- 想最快上手、代码最少:选方案3,用户体验最好,几乎零学习成本。
- 想类型安全、约束严格:选方案2,适合大型项目避免类型错误。
- 想灵活传递闭包变量:选方案1,适合需要把这个闭包在多个地方传递的场景。
内容的提问来源于stack exchange,提问作者Mark A. Donohoe
相关产品推荐
相关产品推荐

