乐趣区

关于swift:Swift-57-中的-any-和-some-译

因为 anysome 都实用于协定,因而我想在这篇博文中将它们放在一起比拟以便更好地解释它们解决别离解决了什么问题,以及在什么状况下应用 anysome 或其余的。

理解 any 和 some 解决的问题

为了解释 any 解决的问题,咱们能够通过一个列子来理解这两个关键字。上面是一个 Pizza 模型的协定:

protocol Pizza {var size: Int { get}
    var name: String {get}
}

在 Swift 5.6,你可能会写上面的这种办法,来接管一个 Pizza

func receivePizza(_ pizza: Pizza) {print("Omnomnom, that's a nice \(pizza.name)")
}

当这个函数被调用时,receivePizza 函数接管一个所谓的披萨协定类型,咱们能够了解为一个披萨盒子。为了晓得这个披萨名称,必须关上这个盒子,也就是获取实现 Pizza 协定的具体对象,而后获取名称。这意味着 Pizza 简直没有编译时优化,这使得 receivePizza 办法调用的开销比咱们想要的更大。

另外上面的函数,看起来如同是一样的

func receivePizza<T: Pizza>(_ pizza: T) {print("Omnomnom, that's a nice \(pizza.name)")
}

不过,这里有一个很次要区别。Pizza 协定在这里没有用作参数类型。它被用作泛型 T 的束缚。这就使得编译器将可能在编译时解析 T 的类型,使得 receivePizza 承受到的是一个具体化的类型。

因为下面这两种办法差别并不是很分明,所以 Swift 团队引入了 any 关键字。此关键字不增加任何新性能。它迫使咱们分明地传播“这是一种存在主义”:(有点拗口,也不是很好了解,我就把他了解成 这么类型的一个货色)

// 下面的第一种写法,减少一个 any 关键字
func receivePizza(_ pizza: any Pizza) {print("Omnomnom, that's a nice \(pizza.name)")
}

应用泛型 T 的示例不须要 any 关键字,因为 Pizza 被用作束缚而不是存在类型。

当初咱们对 any 有了更清晰的理解,持续让咱们钻研一下 some

在 Swift 中,许多开发人员都写如下代码:

let someCollection: Collection

咱们会遇到编译器谬误,通知咱们 Collection 有 Self 或关联的类型要求。在 Swift 5.1 中,咱们能够通知编译器任何拜访 someCollection 的人都不应该关怀这些。他们应该只须要晓得这个货色合乎 Collection 协定,仅此而已。

这种机制对于使 SwiftUI 的 View 协定至关重要。

但也有毛病,那就是在应用 some Colelction 的时候,无奈晓得其中的关联类型是什么。

然而,并非所有协定都有相干的关联类型。再次思考上面这个 receivePizza 版本:

func receivePizza<T: Pizza>(_ pizza: T) {print("Omnomnom, that's a nice \(pizza.name)")
}

咱们定义了一个通用的 T 来容许编译器针对给定的具体类型的 Pizza 进行优化。some 关键字还容许编译器在编译时晓得 some 对象的底层理论类型是什么;它只是对咱们暗藏。这正是 <T: Pizza> 所做的。咱们通过 T 这个类型拜访也只能拜访到 Pizza 协定所公开的内容。这意味着咱们能够重写 receivePizza<T: Pizza>(_:) 如下:

func receivePizza(_ pizza: some Pizza) {print("Omnomnom, that's a nice \(pizza.name)")
}

咱们不再须要 T,也就是咱们不须要创立一个类型来代表咱们的 Pizza。咱们能够说“这个函数须要some Pizza”而不是“这个函数须要一些咱们称之为 T 的 Pizza”。这两种写法是等价的。

抉择 some 还是 any

其实当咱们理解了 some 和 any, 就会晓得这不选其中一个的问题,他们都解决本人的问题的。

一般来说,咱们要尽可能的应用 some 或者泛型,就拿咱们的 Pizza 来说,如果应用 any, 就好比咱们在运行时也会是接管到一个 Pizza 类型的盒子,具体是什么 Pizza, 还须要咱们再关上盒子,然而 some 或者泛型,就会给咱们一个理论的 Pizza 类型了。

实际

让咱们再举一个例子来阐明这一点,这个例子在很大水平上借鉴了我对次要关联类型的解释。

class MusicPlayer {var playlist: any Collection<String> = []

    func play(_ playlist: some Collection<String>) {self.playlist = playlist}
}

在这段代码中,我应用了 some Collection<String> 而不是编写 func play<T: Collection<String>>(_ playlist: T) ,因为泛型只在这一个中央应用。

我的 var playlistany Collection<String> 而不是 some Collection<String> 有两个起因:

  1. 无奈确保编译器将为 play 办法推导的具体 Colection 与为 var playlist 推导的具体 Colection 相匹配;这意味着它们可能不一样。
  2. 编译器首先无奈推断var playlist:some Collection<String>(尝试一下,你会失去一个编译器谬误)

咱们能够用上面的写法来防止应用 any:

class MusicPlayer<T: Collection<String>> {var playlist: T = []

    func play(_ playlist: T) {self.playlist = playlist}
}

然而这样就会强制咱们的 T 为同一类型,比如说咱们在应用时 T 是 Array, 那咱们在 play 办法中就不能在传入其余的 Collection 类型,比如说 Set。然而后面的那种写法是能够的。

总结

尽管 some 和 any 听起来很简单(事实上的确如此),但它们也是 Swift 5.7 中十分弱小和重要的局部。了解他们是很有必要的,因为这能够帮忙咱们更好地了解 Swift 如何解决泛型和协定。

退出移动版