原文:Using tuples as lightweight types in Swift | Swift by Sundell

Swift 的一个真实风趣的功用是能够运用元组(Tuples)创立轻量级容器。这个概念很简略 –元组让你轻松地将任何数量的对象或值组合在一起,而无需创立一个新类型。可是,虽然这是一个简略的概念,它却敞开了一些十分酷的时机,无论是在 API 规划方面还是在结构化代码方面。

在规范库中,元组被用于形成 (key, value) 类型的键值对,当迭代一个字典时,或许当返回插入一个 Set 的成果时(就像咱们上周在Swift 中集合的力气中运用的那样)。本周,让咱们来看看咱们如何在自己的代码中运用元组,以及它们使咱们能够运用的一些技术。

轻量级类型

描绘元组的一种办法是,它们是轻量的、内联的类型。假如你有两个值 — 比方说一个 title 和一个subtitle — 你能够快速地将它们组合成一个新的类型,在你要运用它的当地完全内联,就像这样:

class TextView: UIView {
    func render(_ texts: (title: String, subtitle: String)) {
        titleLabel.text = texts.title
        subtitleLabel.text = texts.subtitle
    }
}

上述办法的一个替代计划是创立一个 struct。虽然在你会在多个当地运用这种类型对的情况下,这可能是更好的挑选,但能够在你实际运用它的当地坚持内联的类型界说,确实有助于坚持工作的简略。它也使得添加额定的特点像改动函数签名一样简略:

class TextView: UIView {
    func render(_ texts: (title: String, subtitle: String, description: String)) {
        titleLabel.text = texts.title
        subtitleLabel.text = texts.subtitle
        descriptionLabel.text = texts.description
    }
}

运用相似上述的办法可能看起来微不足道,但我个人发现,假如我能够在同一个当地输入东西,而不是在代码库中的多个当地跳来跳去来做这样一个简略的改动,这确实对生产力有很大影响。

不过,上述办法的一个缺陷是,假如特点列表开端增加,工作就会变得有点紊乱。处理这个问题的一个办法是运用一个 typealias 来为元组创立一个轻量级的类型界说,同时仍然让工作坚持简略:

class TextView: UIView {
    // 运用 typealias 为元组创立轻量级类型界说
    typealias Texts = (title: String, subtitle: String, description: String)
    func render(_ texts: Texts) {
        titleLabel.text = texts.title
        subtitleLabel.text = texts.subtitle
        descriptionLabel.text = texts.description
    }
}

做到这一点后,假如咱们将来想把元组提取成一个明确的类型 — 比方一个 struct — 也会变得超级简单,由于咱们的代码现在是用 Texts 来引用它。

简化的调用栈点

关于元组的另一个很帅的工作是它们对某个 API 的调用栈点的影响。虽然元组能够有标签,但在创立一个实例时,你总是能够自由地疏忽这些标签。这能够帮助使调用栈点看起来十分漂亮和洁净,例如在处理矢量类型,如坐标时。

比方说,咱们的使用程序有一个在基于块状的地图上存储位置的模型,咱们挑选运用一个元组来界说地图上的坐标,像这样:

struct Map {
    private let width: Int
    private var tiles: [Tile]
    subscript(_ coordinate: (x: Int, y: Int)) -> Tile {
        return tiles[coordinate.x + coordinate.y * width]
    }
}

由于咱们运用了一个元组,咱们现在能够挑选在创立坐标时包含或省掉 xy 标签:

let tileA = map[(x: 1, y: 0)]
let tileB = map[(2, 1)]

等同性

在查看多个值是否持平时,元组也是十分有用的。虽然它们不符合 Equatable 协议(或任何协议),但 Swift 规范库界说了 == 重载,用于包含自身是持平的值的元组。

假定咱们正在构建一个视图控制器,让用户在给定范围内查找其他用户(例如,他们能够挑选只在他们的朋友中查找)。由于咱们不想浪费资源去查找已经被显示出来的成果,咱们能够很简单地运用一个元组来盯梢当前的查找规范,并验证它们自前次查找后是否真的发生了变化,就像这样:

class UserSearchViewController: UIViewController {
    enum Scope {
        case friends
        case favorites
        case all
    }
    // 这儿将用户的查找关键字、查找区域声明成一个元组类型
    private var currentCriteria: (query: String, scope: Scope)?
    func searchForUsers(matching query: String, in scope: Scope) {
        if let criteria = currentCriteria {
            // If the search critera matches what is already rendered, we can
            // return early without having to perform an actual search
            // 假如查找规范与已经出现的内容相符,咱们能够提前返回,而不需要进行实际的查找
            guard (query, scope) != criteria else {
                return
            }
        }
        currentCriteria = (query, scope)
        // Perform search
        ...
    }
}

不需要界说额定的类型,也不需要编写任何 Equatable 完成(虽然期望这很快就会成为曩昔,由于 Swift 很快就会开端为咱们合成大部分的完成)。

与一等类函数结合

将元组与 Swift 支撑一等类函数的事实相结合,能够让咱们做一些十分风趣的工作。事实证明,任何闭包的参数列表实际上都能够用一个元组来描绘,而且由于一等类函数的存在,一切的函数也都是闭包,咱们实际上能够用一个元组来向一个函数传递参数。咱们所要做的就是让 Swift 把一个函数当作一个闭包。为了做到这一点,咱们能够界说一个调用函数,该函数接收任何函数,并将其所需的参数使用于它,像这样:

func call<Input, Output>(_ function: (Input) -> Output, with input: Input) -> Output {
    return function(input)
}

现在咱们假定要实例化一组视图类,它们都能够运用相同类型的参数列表进行初始化:

class HeaderView: UIView {
    init(font: UIFont, textColor: UIColor) {
        ...
    }
}
class PromotionView: UIView {
    init(font: UIFont, textColor: UIColor) {
        ...
    }
}
class ProfileView: UIView {
    init(font: UIFont, textColor: UIColor) {
        ...
    }
}

运用咱们的 call 函数,咱们现在能够简略地使用一个包含预期参数的元组(在这种情况下,咱们想运用创立视图的款式),将每个视图的初始化器作为一个函数传递,并运用咱们的 style 元组调用它。

let styles = (UIFont.systemFont(ofSize: 20), UIColor.red)
let headerView = call(HeaderView.init, with: styles)
let promotionView = call(PromotionView.init, with: styles)
let profileView = call(ProfileView.init, with: styles)

上面的内容有点张狂,但它十分酷!

总结

Swift 中的元组很简略,但十分强大。咱们不只能够用它们来快速界说内联类型,还能够用它们来使查看多个值是否持平这样的使命变得简略得多。最终,结合一等类函数,咱们能够运用元组来将参数列表作为一个值来传递,这真的很风趣。

就像其他 Swift 功用一样,元组与更明确界说的类型(如类和结构)比较有不同的权衡。它们更简单界说,而且十分轻量级,但因而它们不能做一些工作,比方存储弱引用或在其中包含任何类型的逻辑(比方具有办法)。一方面,这是一个缺陷,但它也有助于在处理更简略的数据容器时坚持简略性。

你是怎么想的?你目前是否在你的代码中运用元组,或许是你要尝试的东西?让我知道,以及你可能有的任何其他问题、评论或反馈,请在 Twitter 上 @johnsundell。

谢谢你的阅览!