阅读 101

Swift:where关键词使用

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文同时参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

初识where

Swift中where这个关键词,你或许见过。

我们先来一个简单的例子,Sequence协议中的源码:

public protocol Sequence {
    /// A type representing the sequence's elements.
    associatedtype Element where Self.Element == Self.Iterator.Element
    
    /// A type that provides the sequence's iteration interface and
    /// encapsulates its iteration state.
    associatedtype Iterator : IteratorProtocol
}
复制代码

associatedtype Element where Self.Element == Self.Iterator.Element这段代码的意思是关联类型Element需要和关联类型Iterator中的Element是同种类型。

where在这里表示强约束。

如果你曾经手写过SQL,对下面这段SQL肯定有理解:

let sql = "SELECT * FROM messageTable WHERE \(adminId) = ? AND \(companyId) = ? AND (\(self.typeId) = 0 OR \(self.typeId) = 1)"
复制代码

以上代码是我用Swift写最原始的SQL语句,大概意思就是从表里获取任意有adminId并且有companyId并且typeId为0或者1的数据,可以看见这个SQL有一个大大WHERE

对比Swift中的where和SQL中的WHERE从语义上是不是非常相似呢?

注意:SQL中WHERE也可以写成where。

那么Swift中的where有哪些用法呢?

where用于泛型约束

先上一个例子我们来说明一下:

/// 定义一个协议
protocol Animal {

}

/// 定义一个Cat类型,
struct Cat<T>: Animal {
    /// 猫需要玩伴,这里我们用泛型T表示
    let playmate: T
}
复制代码

如果我们想对泛型T做更细化的约束说明,比如说我们希望猫的玩伴也是一个动物,我们怎么写呢?

我们可以直接这么写:

struct Cat<T: Animal>: Animal {
    let playmate: T
}
复制代码

也可以这么写:

struct Cat<T>: Animal where T: Animal {
    let playmate: T
}
复制代码

两者都是对泛型T的约束。

where用于指明类型

/// 我们有一个Person类型
struct Person {
    let age: Int
    let name: String
}

/// 我组合成了一个Person数组
let p1 = Person(age: 10, name: "season")
let p2 = Person(age: 20, name: "soso")
let p3 = Person(age: 30, name: "sola")

let array = [p1, p2, p3]
复制代码

我想一口气打印[Person]这种数组中Person的name该怎么做?

你肯定会说,这有何难?map一下就可以啦:

[p1, p2, p3].map { $0.name }
复制代码

但是如果[Person]数组的打印name的方法要经常使用,希望能够封装成为一个方法,应该怎么办呢?

这个时候,祭出where,写一个Array的分类再好不过了:

extension Array where Element == Person {
    func printName() {
        map {
            print($0.name)
        }
    }
}
复制代码

使用的时候直接[p1, p2, p3].printName()这么调用就好了。

where用于for循环的条件判断

还是上面let array = [p1, p2, p3]这个例子,我们希望age为20的人做其他的业务处理,而其他的人不动。

一般我们可能会这么写:

for person in array {
    if person.age == 20 {
        /// 做其他的业务处理
    }
}
复制代码

但是我们通过where可以有更简洁的写法:

for person in array where person.age == 20 {
    /// 做其他的业务处理
}
复制代码

对比,两个for循环,更中意哪一个呢?

当然,另外有一个思路是先使用高级函数filter或者满足条件的元素,再做其他的业务处理。

总结

Swift的where和SQL中的WHERE有那么点相似的味道。

Swift中where常见的用发如下:

  • 用于泛型约束,其实很多泛型约束可以通过T: XXX这种形式表示,但是当有associatedtype关联类型时,where的价值才显示出来。

    比如我们来一个Collection协议的例子:

    public protocol Collection : Sequence {
    
        @available(*, deprecated, message: "all index distances are now of type Int")
    
        typealias IndexDistance = Int
    
        associatedtype Element
    
        /// A type that represents a position in the collection.
        ///
        /// Valid indices consist of the position of every element and a
        /// "past the end" position that's not valid for use as a subscript
        /// argument.
        associatedtype Index : Comparable where Self.Index == Self.Indices.Element, Self.Indices.Element == Self.Indices.Index, Self.Indices.Index == Self.SubSequence.Index, Self.SubSequence.Index == Self.Indices.Indices.Element, Self.Indices.Indices.Element == Self.Indices.Indices.Index, Self.Indices.Indices.Index == Self.SubSequence.Indices.Element, Self.SubSequence.Indices.Element == Self.SubSequence.Indices.Index, Self.SubSequence.Indices.Index == Self.Indices.Indices.Indices.Element, Self.Indices.Indices.Indices.Element == Self.Indices.Indices.Indices.Index, Self.Indices.Indices.Indices.Index == Self.SubSequence.Indices.Indices.Element, Self.SubSequence.Indices.Indices.Element == Self.SubSequence.Indices.Indices.Index, Self.SubSequence.Indices.Indices.Index == Self.SubSequence.Indices.Indices.Indices.Element, Self.SubSequence.Indices.Indices.Indices.Element == Self.SubSequence.Indices.Indices.Indices.Index
    }
    复制代码

    请用力拖动代码块,看看Index的例子。

  • 在用于泛型约束的同时,where也可以直接用于泛型指明,明确类型,这样更有助编写函数。

  • where编写函数中对于泛型参数的约束与指明在官方写的API中比比皆是:

    public func dynamicTypeSize<T>(_ range: T) -> some View where T : RangeExpression, T.Bound == DynamicTypeSize
    复制代码
  • where可以用于在for循环中对其条件进行约束进而简化代码,提高语义。


大家在平时的使用的中还有哪些使用where的心得与体会,欢迎讨论与抛砖。

请回答我,Collection协议的Index你看跪了么,有没有大佬给我讲讲?


我们下期见。

文章分类
iOS
文章标签