在Swift 5.6的更新list里面看到了existential any的简略介绍,但是由于过于简略导致一头雾水,经过一些查验,这里说一下对这个改动的了解。SE-0335
举例简述
一般咱们在运用协议时,是为了确保类型的一致性并遵守一些要求,例如完成某个办法:
protocol ProtocolA {
func test()
}
struct Test: ProtocolA {
func test() {
print("hello")
}
}
let t = Test()
t.test() /// Static Dispatch
此刻的t
是清晰类型的,Swift会对t.test()
的调用运用Static Dispatch,功率更高。
如果咱们由于某些状况下无法清晰类型(的确会出现这种状况),还想调用test
办法,那么咱们会出现这种写法:
let t: ProtocolA = Test()
t.test() /// Dynamic Dispatch
此刻的t
的类型咱们就能够称作existential type,Swift会运用Dynamic Dispatch完成test
调用,当时场景是V-Table调用,这种功率要低于静态派发。
所以在SE-0335中新增了any
关键字来处理existential type的状况,上面的写法在5.6中应该为:
let t: any ProtocolA = Test()
t.test() /// Static Dispatch
此刻就会走静态派发了。
进一步举例
或许你觉得这种场景很少见,那么我再举两个例子
例子1
struct A {
var obs: [ProtocolA]?
}
let a = A()
a.obs?.map {
/// dynamic dispatch
$0.test()
}
struct B {
/// 引进any关键字
var obs: [any ProtocolA]?
}
let b = B()
b.obs?.map {
/// static dispatch
$0.test()
}
能够看到,在日常开发中,上面这个场景经常会遇到,原有的写法会对每个ob
进行动态派发,而引进了any
关键字后,变为了静态派发,提高了功率。
例子2
第二个场景是本人在写基础组件时遇到过的疑惑,能够先看下代码:
func test1(obj: ProtocolA) {
obj.test()
}
func test2<T: ProtocolA>(obj: T) {
obj.test()
}
之前很疑惑这两种写法的差异,由于编译层面看没什么差异(当办法运用泛型作为返回值时能够清晰类型)。现在,通过上面的分析,咱们能够了解了,test1
中办法调用是动态派发,而test2
的办法调用是静态派发,明显后者是更为科学的写法,然后咱们在运用的时候前者却更容易了解,所以any
关键字就派上用场了:
func test(obj: any ProtocolA) {
obj.test() /// Static Dispatch
}
以上。
后话
目前Xcode 13.3能够运用此特性。对于此特性,按照苹果的计划,在未来的Swift 5.x版本中,将对涉及到的地方进行正告,而在Swift 6开端会直接抛出错误。
这种改变有或许在未来损坏现有代码,所以在过渡期应该尽或许地去运用正确的方式。在个人看来这个改动非常重要,通过了解的过程也能够加深对Protocol类型的了解。