每天了解一点不一样的 Swift 小知识

代码截图

image.png

小笔记

为什么只用传递一个 < 就实现了功能

与 Objective-C 不同的是,< 在 Swift 中是一个独立的函数,与其他的函数一样,类似 < 这样的操作符也拥有函数参数和函数返回值。

我们看一下 < 的定义

image.png

它其实本质就是一个返回值为 (lhs: Self, rhs: Self) -> Bool 的函数,所以将今天的代码进行如下改造:

let ascending: (Int, Int) -> Bool = { (lhs, rhs) -> Bool in
return lhs > rhs
}
let sorted = array.sorted(by: ascending)

这段代码里 ascending 扮演的功能与 < 完全一样,只是用了一个更容易理解的方式写代码。

当然你也可以这么理解 <

let sorted = array.sorted(by: { (lhs, rhs) -> Bool in
return lhs > rhs
})

这种把函数当做入参的方式,在 Swift 的一些高阶函数使用中经常可以遇到,应该是需要了解的一个知识点。

这里推两篇拓展阅读:HIGHER-ORDER FUNCTIONS IN SWIFTSwift Guide to Map Filter Reduce

我好像还看到过 array.sorted{ $0 < $1 } 的写法

array.sorted{ $0 < $1 }array.sorted(by: <) 本质上没有任何区别,即运算结果一致,只是利用了不同的 Swift 语言特性,使其在最终的展示层面出现了一些变化。

这里我们来分析下代码是如何一步步变成 array.sorted{ $0 < $1 } 的。其中涉及的知识点主要有 4 个

  1. 隐式返回函数
  2. 尾随闭包
  3. 类型推断
  4. 参数名称缩写

sorted 最原始的代码形式应该是这样的:

let sortedA = array.sorted(by: { (lhs, rhs) -> Bool in
return lhs > rhs
})

隐式返回的函数

隐式返回的函数是指:如果一个函数的整个函数体是一个单行表达式,那么就可以隐式地返回这个表达式,也就是说我们可以省略像 return 这样的关键字。

结合原始代码的实际情况,它将变成如下的形式:

let sortedB = array.sorted(by: { (lhs, rhs) -> Bool in
lhs > rhs
})

尾随闭包

尾随闭包是一个书写在函数圆括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签。

结合上面的代码,我们可以将其转换为如下的形式:

let sortedC = array.sorted(){ (lhs, rhs) -> Bool in
lhs > rhs
}

不过这还不最简洁的,如果闭包参数是传给函数的唯一参数,你还可以完全忽略括号。

let sortedD = array.sorted{ (lhs, rhs) -> Bool in
lhs > rhs
}

类型推断

凭借着 Swift 强大的类型推断能力,上面的代码在还可以进一步简化成如下的形式。

let sortedE = array.sorted{ lhs, rhs in lhs > rhs }

参数名称缩写

在 Swift 中,可以通过参数名称缩写而不是参数名字来引用参数

如果在闭包表达式中使用参数名称缩写,就可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。in 关键字也同样可以被省略,此时闭包表达式完全由闭包函数体构成:

let sortedF = array.sorted{ $0 > $1 }

拓展阅读: Shorthand Argument Names in Swift

最后说点什么?

Swift 的这些特性使其可以写出十分简洁的代码,让很多程序员爱不释手,但有时候过多的”美化”,反而增加了一些阅读门槛。

这里不妨举 2 个例子

let value: Int? = 1
// Solution A
let newValue = value.map { $0 + 1 }
// Solution B
let newValue: Int?
if let value = value {
newValue = value + 1
}

在这里,map 函数被用来处理可选值。

// Solution A
let purpleView: UIView = {
$0.backgroundColor = .purple
return $0
}(UIView())
// Solution B
let purpleView: UIView = {
let view = UIView()
view.backgroundColor = .purple
return view
}()

而这里利用了参数名称缩写的方式来简化闭包内部的实现。

以上代码到底是 Solution A 好还是 Solution B 好,我确实无法给出一个完美的答案,所以如何写出一份让所有人都觉得阅读体验良好的代码,或许会成为 Swifter 开发者甜蜜的烦恼吧。