閉包
閉包表達(dá)式
var fn = {
(v1: Int, v2: Int) -> Int in
return v1 + v2
}
fn(10, 20)
{
(v1: Int, v2: Int) -> Int in
return v1 + v2
}(10, 20)
可以這樣定義一個(gè)閉包表達(dá)式
{
(參數(shù)列表) -> 返回值類型 in
函數(shù)體代碼
}
閉包表達(dá)式的簡寫
定義一個(gè)方法
func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
print(fn(v1, v2))
}
調(diào)用這個(gè)方法
完整寫完是這樣的
exec(v1: 10, v2: 20, fn: {
(v1: Int, v2: Int) -> Int in
return v1 + v2
})
可以將類型省略
exec(v1: 10, v2: 20, fn: {
v1, v2 in return v1 + v2
})
也可以將return 省略
exec(v1: 10, v2: 20, fn: {
v1, v2 in v1 + v2
})
使用美元符號(hào)$ 代替參數(shù)
exec(v1: 10, v2: 20, fn: {$0 + $1})
最終可以省略參數(shù), 直接使用加號(hào)
exec(v1: 10, v2: 20, fn: +)
尾隨閉包
如果將一個(gè)很長的閉包表達(dá)式作為函數(shù)的最后一個(gè)實(shí)參, 使用尾隨閉包可以增強(qiáng)函數(shù)可讀性
尾隨閉包是一個(gè)被書寫在函數(shù)調(diào)用括號(hào)外面的閉包表達(dá)式
如果閉包表達(dá)式是函數(shù)的唯一實(shí)參, 而且使用了尾隨閉包的語法, 就不需要再函數(shù)名后邊加圓括號(hào)了
exec(v1: 10, v2: 20) { v1, v2 in
v1 + v2
}
exec(v1: 10, v2: 20) { $0 + $1 }
唯一實(shí)參
func exec(fn: (Int, Int) -> Int) {
print(fn(1, 2))
}
exec(fn: {
$0 + $1
})
exec(fn: {
$0 + $1
})
exec {
$0 + $1
}
swift 中的數(shù)組排序sort 函數(shù)
@inlinable public func sorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element]
let nums = [11, 2, 6, 5, 18, 45, 99, 65]
// 自己定一個(gè)比較
func cmp(i1: Int, i2: Int) -> Bool {
// 值比較大的放在左邊
return i1 > i2
}
let arr = nums.sorted(by: cmp(i1:i2:))
print("\(#line) ~~~~~~ \(#function) ~~~~~~ \(arr)")
sort 傳入?yún)?shù)的簡寫
nums.sorted(by:{
(i1: Int, i2: Int) -> Bool in
return i1 > i2
})
nums.sorted(by: {
i1, i2 in
i1 > i2
})
nums.sorted(by: {
$0 > $1
})
nums.sorted(by: >)
nums.sorted() {
$0 > $1
}
nums.sorted {
$0 > $1
}
閉包
一個(gè)函數(shù)和它所捕獲的變量\常量環(huán)境組合起來, 稱為閉包
func getFn() -> (Int) -> Int {
var num = 0
func plus(_ i: Int) -> Int {
num += i
return num
}
return plus(_:)
}
var fn1 = getFn()
fn1(1)
fn1(2)
fn1(3)
fn1(4)
// 1 3 6 10
fn 捕獲了變量num
如果不捕獲num, 直接返回i, 調(diào)用函數(shù), 則為以下結(jié)果
func getFn() -> (Int) -> Int {
var num = 0
func plus(_ i: Int) -> Int {
return i
}
return plus(_:)
}
rax 通常保存返回值
讀取rax 值
register read rax
rax = 0x0000000100003df0 TestSwift`plus(Swift.Int) -> Swift.Int at main.swift:19

恢復(fù)代碼, 返回num, 則匯編為

alloc 申請一塊內(nèi)存, 將num 拷貝到堆中
看下rax 返回值


可以看到x/5xg 0x0000000103827eb0
第三組中為0x0000000000000001, 初始化為了1.

繼續(xù)跳過一個(gè)斷點(diǎn), 賦值為了0x0000000000000003
(lldb) x/5xg 0x0000000103827eb0
0x103827eb0: 0x0000000100004018 0x0000000200000003
0x103827ec0: 0x0000000000000003 0x0000000000000000
0x103827ed0: 0x0000000000000000
如何看到alloc 申請了多少內(nèi)存空間給fn 呢?
需要24 個(gè)字節(jié)存儲(chǔ)數(shù)據(jù), 但是真正給的空間為32 字節(jié), 16 的倍數(shù)

可以將閉包想想成一個(gè)類的實(shí)例對象
- 內(nèi)存在堆空間
- 捕獲的局部變量就是對象的成員
- 組成閉包的函數(shù)就是類內(nèi)部定義的方法
class Closure {
var num = 0
func plus(_ i: Int) -> Int {
num += i
return i
}
}
var cs1 = Closure()
cs1.plus(1)

調(diào)用到rax, 即為getFn 函數(shù)地址, jmp 為plus 地址0x100003ed0

每次調(diào)用fn1, 調(diào)用的都為plus, 同一個(gè)函數(shù)地址, 第三個(gè)字節(jié)存儲(chǔ)的是num 的地址
如果num 為全局變量, 則

如果如下代碼
typealias Fn = (Int) -> (Int, Int)
func getFns() -> (Fn, Fn) {
var num1 = 0
var num2 = 0
func plus(_ i: Int) -> (Int, Int) {
num1 += i
num2 += i << 1
return (num1, num2)
}
func minus(_ i: Int) -> (Int, Int) {
num1 -= i
num2 -= i >> 1
return (num1, num2)
}
return (plus, minus)
}
let (p, m) = getFns()
p(6) // (6, 12)
m(5) // (1, 10)
p(4) // (5, 18)
m(3) // (2, 17)
num1 和num2, 分別alloc 一次, 給兩個(gè)函數(shù)共用
自動(dòng)閉包
使用@autoclosure, 會(huì)將() -> T 格式的參數(shù)自動(dòng)封裝成閉包, 自動(dòng)閉包也支持中間的參數(shù), 有無自動(dòng)閉包@autoclosure, 可以構(gòu)成函數(shù)重載
例如下面代碼, 如果第一個(gè)參數(shù)已經(jīng)大于0, 則沒必要調(diào)用第二個(gè)參數(shù)的內(nèi)容
// 如果第一個(gè)數(shù)大于0, 返回第一個(gè), 否則返回第二個(gè)
func getFirstPositive(_ v1: Int, _ v2: Int) -> Int {
return v1 > 0 ? v1 : v2
}
getFirstPositive(10, 20)
getFirstPositive(-2, 20)
getFirstPositive(0, -4)
改寫成下面, 將第二個(gè)參數(shù)修改為一個(gè)閉包函數(shù)
// 如果第一個(gè)參數(shù)已經(jīng)大于0, 則第二個(gè)參數(shù)不必調(diào)用執(zhí)行
func getFirstPositive(_ v1: Int, _ v2: () -> Int) -> Int? {
return v1 > 0 ? v1 : v2()
}
在swift 中可以使用@autoclosure, 將它封裝成一個(gè)自動(dòng)閉包
// 可以使用自動(dòng)閉包來實(shí)現(xiàn)
func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int? {
return v1 > 0 ? v1 : v2()
}
getFirstPositive(10, 20)
則在調(diào)用時(shí), 最后一個(gè)參數(shù)自動(dòng)封裝成{20}