0

Swift:where关键词使用

 2 years ago
source link: https://juejin.cn/post/7017605307593392159
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
2021年10月11日 阅读 79

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你看跪了么,有没有大佬给我讲讲?


我们下期见。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK