小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本文同时参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
初识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你看跪了么,有没有大佬给我讲讲?
我们下期见。