Swift Tips 008 - Passing functions & operators as closures
每天了解一点不一样的 Swift 小知识
代码截图
小笔记
为什么只用传递一个 <
就实现了功能
与 Objective-C 不同的是,<
在 Swift 中是一个独立的函数,与其他的函数一样,类似 <
这样的操作符也拥有函数参数和函数返回值。
我们看一下 <
的定义
它其实本质就是一个返回值为 (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 SWIFT 和 Swift Guide to Map Filter Reduce
我好像还看到过 array.sorted{ $0 < $1 }
的写法
array.sorted{ $0 < $1 }
和 array.sorted(by: <)
本质上没有任何区别,即运算结果一致,只是利用了不同的 Swift 语言特性,使其在最终的展示层面出现了一些变化。
这里我们来分析下代码是如何一步步变成 array.sorted{ $0 < $1 }
的。其中涉及的知识点主要有 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 开发者甜蜜的烦恼吧。