我想有一种通用的方式来做一些事情,就像在 Swift 3 中一样:
public protocol Callable {
associatedtype In : CVarArg
associatedtype Out : CVarArg
}
public struct IntCallable : Callable {
public typealias In = Int
public typealias Out = Double
public typealias FunctionalBlock = @convention(c) (In) -> Out
public func call(_ block: FunctionalBlock) { /* do stuff */ }
}
所以我希望它看起来更像这样:
public protocol Callable {
associatedtype In : CVarArg
associatedtype Out : CVarArg
typealias FunctionalBlock = @convention(c) (In) -> Out
}
public struct IntCallable : Callable {
public typealias In = Int
public typealias Out = Double
}
public extension Callable {
public func call(_ block: FunctionalBlock) { /* do stuff */ }
}
但是,我收到错误:
'(Self.In) -> Self.Out' is not representable in Objective-C, so it cannot be used with '@convention(c)'
我可以对 In/Out 关联类型施加任何约束,以允许我声明功能块的泛型形式?它在没有@convention(c)
的情况下工作正常,但我需要它来形成 C 函数调用。
这在 Swift 中目前是不可能的,因为 Swift 管理作为协议传递的值的方式,而CVarArg
是一个协议。
幕后发生的事情是,当在协议的保护伞下传递值时,Swift 编译器会创建一个包装值的存在容器,该值在被调用方站点透明地解包。
所以基本上你的块实际上看起来像这样:
typealias FunctionalBlock = @convention(c) (Container<In>) -> Container<Out>
由于这种幕后转换,您不会传递可以用 C 表示的值,因此会出现错误。
这与其他协议相关问题非常相似,例如著名的协议不符合自身?
最好的办法是为符合 CVarArg 的所有类型添加重载,因为这是一个有限且不可更改的列表。