假设我们具有以下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
来手动调用网桥,并希望通过Protocol
对其进行某种操作来到达as!
对象。可以吗?
print(Sub.self as AnyObject) // <Protocol: 0x012345678>
嗯,那很有希望。当我在通用参数上尝试使用它时?
print(proto as AnyObject) // Terminated due to signal: SEGMENTATION FAULT (11)
哦,快来。
我怀疑这可能是编译器中的错误,并且我计划测试一些事情以确定是否是这种情况,但是由于Swift来源需要地质年代才能进行编译,因此我想我会在此发布此内容,我在等待。任何人都对这里发生的事情有任何见解和/或解决方法?
答案 0 :(得分:2)
我知道它确实不漂亮而且“敏捷”,但是可以使用Protocol
来实现将Objective-C foo
适当地传递到NSProtocolFromString
的目的:>
let proxy2 = foo(NSProtocolFromString(String(reflecting: proto))!)
其中String(reflecting:)
是获得适用于通过NSProtocolFromString
解析的完全限定类型名称的有用方法。
我会说您遇到的崩溃是一个错误。
答案 1 :(得分:2)
好的,在进一步研究之后,我确定它确实是编译器错误,并且有filed a report on it: SR-8129。似乎正在发生的事是,Swift编译器错误地假设proto
始终是具体class
类型的元类型,因此它通过发出对swift_getObjCClassFromMetadata
的调用来执行桥接遇到协议元类型时崩溃。将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)
}
不是最漂亮的东西,但是可能比通过字符串操作查找协议更可取。