将*通用*协议桥接到 Objective-C 的解决方法?
Workaround to bridge a *generic* protocol to Objective-C?
假设我们有以下 Objective-C API:
- (id)foo:(Protocol *)proto;
导入 Swift 为:
func foo(_ proto: Protocol) -> Any
是的,这是为我们提供代理对象的原因之一。这些在 Swift 中使用起来往往很烦人,所以假设我们想围绕这个东西做一个包装,让它更友好一些。首先我们定义几个 Objective-C 兼容的协议:
@objc protocol Super {}
@objc protocol Sub: Super {}
现在,我们定义一个函数,它接受一个符合Super
的协议并将其传递给foo()
,然后我们以Sub
作为参数调用它,看看是否有效:
func bar<P: Super>(proto: P.Type) {
let proxy = foo(proto)
// do whatever with the proxy
}
bar(proto: Sub.self)
好吧,这无法编译。给出的错误信息是:
error: cannot convert value of type 'P.Type' to expected argument type 'Protocol'
这里有一些(主要)编译的东西:
func bar<P: Super>(proto: P.Type) {
// when called with 'Sub.self' as 'proto':
print(type(of: proto)) // Sub.Protocol
print(type(of: Sub.self)) // Sub.Protocol
print(proto == Sub.self) // true
let proxy1 = foo(Sub.self) // compiles, runs, works
let proxy2 = foo(proto) // error: cannot convert value of type 'P.Type' to expected argument type 'Protocol'
}
好的,它几乎在所有方面都与 Sub.self
相同,只是我不能将它传递给需要 Objective-C 协议的东西。嗯。
问题在于,尽管 Sub
符合 Super
意味着它必须是 Objective-C 协议,但编译器并未意识到这一点。我们可以解决这个问题并手动桥接它吗?好吧,让我们看看Protocol
的界面,看看有什么我们可以...
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
@interface Protocol : NSObject
@end
哦。嗯。
好吧,Protocol
是一个完整的 NSObject
子类这一事实表明,这可能是我最喜欢的功能的全部成果,神奇的 Swift<-> Objective-C 对事物执行非平凡转换的桥梁,而不是显而易见的事情。好吧,这至少给了我一个想法;我应该能够通过强制转换为 AnyObject
来手动调用桥,并希望通过 as!
ing 或其他方式以这种方式获得 Protocol
对象。有用吗?
print(Sub.self as AnyObject) // <Protocol: 0x012345678>
好吧,这很有希望。当我在我的通用参数上尝试它时?
print(proto as AnyObject) // Terminated due to signal: SEGMENTATION FAULT (11)
哦,来。
我怀疑这可能是编译器中的一个错误,我打算测试一些东西以确定是否是这种情况,但是由于 Swift 源代码需要一个地质时代来编译,我想我我在等待的时候 post 在这里。有人对这里发生的事情有任何见解 and/or 解决方法吗?
我知道它不漂亮 "Swifty",确切地说,但是可以使用 NSProtocolFromString
如下:
let proxy2 = foo(NSProtocolFromString(String(reflecting: proto))!)
其中 String(reflecting:)
是获取适合通过 NSProtocolFromString
解析的完全限定类型名称的有用方法。
我会说你遇到的崩溃是一个错误。
好的,在进一步调查之后,我确定它确实是一个编译器错误,并且 filed a report on it: SR-8129。似乎正在发生的是 Swift 编译器错误地假设 proto
将始终是具体 class
类型的元类型,因此它通过发出对 [=13 的调用来执行桥接=],它在遇到协议元类型时崩溃。当 Sub.self
显式转换为 AnyObject
时,编译器会发出 Swift._bridgeAnythingToObjectiveC<A>(A) -> Swift.AnyObject
,这似乎是动态确定对象的类型并相应地桥接它。
考虑到这一点,解决方法就显而易见了:首先将 proto
转换为 Any
,以破坏与泛型关联的类型信息并强制编译器发出 Swift._bridgeAnythingToObjectiveC<A>(A) -> Swift.AnyObject
。事实上,这似乎有效:
func bar<P: Super>(proto: P.Type) {
foo(proto as Any as AnyObject as! Protocol)
}
不是最漂亮的东西,但可能比通过字符串操作查找协议更可取。
假设我们有以下 Objective-C API:
- (id)foo:(Protocol *)proto;
导入 Swift 为:
func foo(_ proto: Protocol) -> Any
是的,这是为我们提供代理对象的原因之一。这些在 Swift 中使用起来往往很烦人,所以假设我们想围绕这个东西做一个包装,让它更友好一些。首先我们定义几个 Objective-C 兼容的协议:
@objc protocol Super {}
@objc protocol Sub: Super {}
现在,我们定义一个函数,它接受一个符合Super
的协议并将其传递给foo()
,然后我们以Sub
作为参数调用它,看看是否有效:
func bar<P: Super>(proto: P.Type) {
let proxy = foo(proto)
// do whatever with the proxy
}
bar(proto: Sub.self)
好吧,这无法编译。给出的错误信息是:
error: cannot convert value of type 'P.Type' to expected argument type 'Protocol'
这里有一些(主要)编译的东西:
func bar<P: Super>(proto: P.Type) {
// when called with 'Sub.self' as 'proto':
print(type(of: proto)) // Sub.Protocol
print(type(of: Sub.self)) // Sub.Protocol
print(proto == Sub.self) // true
let proxy1 = foo(Sub.self) // compiles, runs, works
let proxy2 = foo(proto) // error: cannot convert value of type 'P.Type' to expected argument type 'Protocol'
}
好的,它几乎在所有方面都与 Sub.self
相同,只是我不能将它传递给需要 Objective-C 协议的东西。嗯。
问题在于,尽管 Sub
符合 Super
意味着它必须是 Objective-C 协议,但编译器并未意识到这一点。我们可以解决这个问题并手动桥接它吗?好吧,让我们看看Protocol
的界面,看看有什么我们可以...
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
@interface Protocol : NSObject
@end
哦。嗯。
好吧,Protocol
是一个完整的 NSObject
子类这一事实表明,这可能是我最喜欢的功能的全部成果,神奇的 Swift<-> Objective-C 对事物执行非平凡转换的桥梁,而不是显而易见的事情。好吧,这至少给了我一个想法;我应该能够通过强制转换为 AnyObject
来手动调用桥,并希望通过 as!
ing 或其他方式以这种方式获得 Protocol
对象。有用吗?
print(Sub.self as AnyObject) // <Protocol: 0x012345678>
好吧,这很有希望。当我在我的通用参数上尝试它时?
print(proto as AnyObject) // Terminated due to signal: SEGMENTATION FAULT (11)
哦,来。
我怀疑这可能是编译器中的一个错误,我打算测试一些东西以确定是否是这种情况,但是由于 Swift 源代码需要一个地质时代来编译,我想我我在等待的时候 post 在这里。有人对这里发生的事情有任何见解 and/or 解决方法吗?
我知道它不漂亮 "Swifty",确切地说,但是可以使用 NSProtocolFromString
如下:
let proxy2 = foo(NSProtocolFromString(String(reflecting: proto))!)
其中 String(reflecting:)
是获取适合通过 NSProtocolFromString
解析的完全限定类型名称的有用方法。
我会说你遇到的崩溃是一个错误。
好的,在进一步调查之后,我确定它确实是一个编译器错误,并且 filed a report on it: SR-8129。似乎正在发生的是 Swift 编译器错误地假设 proto
将始终是具体 class
类型的元类型,因此它通过发出对 [=13 的调用来执行桥接=],它在遇到协议元类型时崩溃。当 Sub.self
显式转换为 AnyObject
时,编译器会发出 Swift._bridgeAnythingToObjectiveC<A>(A) -> Swift.AnyObject
,这似乎是动态确定对象的类型并相应地桥接它。
考虑到这一点,解决方法就显而易见了:首先将 proto
转换为 Any
,以破坏与泛型关联的类型信息并强制编译器发出 Swift._bridgeAnythingToObjectiveC<A>(A) -> Swift.AnyObject
。事实上,这似乎有效:
func bar<P: Super>(proto: P.Type) {
foo(proto as Any as AnyObject as! Protocol)
}
不是最漂亮的东西,但可能比通过字符串操作查找协议更可取。