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 typeSwift会运用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类型的了解。