一. 函數(shù)的定義
- 無返回值

- 有返回值
形參默認(rèn)是let,也只能是let

注意:Swift中可以使?func定義?個(gè)函數(shù),也可以使?閉包表達(dá)式定義?個(gè)函數(shù)。
- 隱式返回(Implicit Return)
如果整個(gè)函數(shù)體是一個(gè)單一表達(dá)式,那么函數(shù)會隱式返回這個(gè)表達(dá)式

- 返回元組:實(shí)現(xiàn)多返回值

二. 函數(shù)的文檔注釋
格式如下:

盡量遵守注釋,這樣按住optional會有提示:

更多關(guān)于函數(shù)文檔注釋可參考:https://swift.org/documentation/api-design-guidelines/
三. 參數(shù)
1. 參數(shù)標(biāo)簽(Argument Label)
默認(rèn)參數(shù)標(biāo)簽是參數(shù)名,如果設(shè)置了參數(shù)標(biāo)簽會覆蓋默認(rèn)標(biāo)簽。

設(shè)置參數(shù)標(biāo)簽是為了可讀性,使函數(shù)調(diào)用讀起來更像個(gè)英文句子。
上面的at是為了讀起來更通順,上面的time是為了讓你理解傳進(jìn)來的參數(shù)是什么。
可以使用下劃線 _ 省略參數(shù)標(biāo)簽。

不推薦省略參數(shù)標(biāo)簽,因?yàn)橛袝r(shí)候省略了就不知道傳的是哪個(gè)參數(shù)了,如下:

2. 默認(rèn)參數(shù)值(Default Parameter Value)
參數(shù)可以有默認(rèn)值,調(diào)?帶有默認(rèn)參數(shù)的函數(shù)時(shí),帶有默認(rèn)參數(shù)的參數(shù)可以不傳,其他參數(shù)?定要傳。

C++的默認(rèn)參數(shù)值有個(gè)限制:必須從右往左設(shè)置。由于Swift擁有參數(shù)標(biāo)簽,因此并沒有此類限制。
3. 可變參數(shù)(Variadic Parameter)
一個(gè)函數(shù)最多只能有1個(gè)可變參數(shù)。

緊跟在可變參數(shù)后面的參數(shù)不能省略參數(shù)標(biāo)簽,因?yàn)闀衅缌x。

- Swift自帶的print函數(shù)

可以發(fā)現(xiàn),print函數(shù)傳三個(gè)參數(shù)
第一個(gè)參數(shù)是可變參數(shù),省略了標(biāo)簽。
第二個(gè)參數(shù)是分隔符,默認(rèn)是空格。
第三個(gè)參數(shù)是兩個(gè)打印之間的結(jié)束符,默認(rèn)是以換行結(jié)束。
如果需要給第二個(gè)第三個(gè)參數(shù)傳值,那么第二個(gè)第三個(gè)參數(shù)的標(biāo)簽不能省略標(biāo)簽。
4. 輸入輸出參數(shù)(In-Out Parameter)
可以用inout定義一個(gè)輸入輸出參數(shù):可以在函數(shù)內(nèi)部修改外部實(shí)參的值。

輸入輸出函數(shù)調(diào)用的時(shí)候需要加&
其實(shí)用于交換外部實(shí)參的值更簡單的方法是使用元祖,如下:

注意:
可變參數(shù)不能標(biāo)記為inout
inout參數(shù)不能有默認(rèn)值
inout參數(shù)只能傳入可以被多次賦值的
inout參數(shù)的本質(zhì)是地址傳遞(引用傳遞)
inout參數(shù)只能傳入可以被多次賦值的是什么意思?
如果傳入的是字面量40或者let修飾的參數(shù),字面量不能改,let修飾的參數(shù)只能賦值一次,所以如果傳入字面量40或者let修飾的參數(shù)就達(dá)不到在函數(shù)內(nèi)部可以修改的目的,所以不能傳入。
其實(shí)蘋果提供了用于交換的函數(shù)swap,上面為了不跟蘋果提供的函數(shù)沖突才起名swapValues。
- 驗(yàn)證inout參數(shù)的本質(zhì)是地址傳遞
MJ老師是通過匯編驗(yàn)證的,我看不懂,直接查看蘋果提供的swap函數(shù)源碼:
@inlinable
public func swap<T>(_ a: inout T, _ b: inout T) {
// Semantically equivalent to (a, b) = (b, a).
// Microoptimized to avoid retain/release traffic.
let p1 = Builtin.addressof(&a)
let p2 = Builtin.addressof(&b)
_debugPrecondition(
p1 != p2,
"swapping a location with itself is not supported")
// Take from P1.
let tmp: T = Builtin.take(p1)
// Transfer P2 into P1.
Builtin.initialize(Builtin.take(p2) as T, p1)
// Initialize P2.
Builtin.initialize(tmp, p2)
}
很容易看出的確是地址傳遞。
四. 函數(shù)重載(Function Overload)
函數(shù)重載的規(guī)則:
1.函數(shù)名相同
2.參數(shù)個(gè)數(shù)不同 || 參數(shù)類型不同 || 參數(shù)標(biāo)簽不同
重載和重寫不一樣,重載牽扯不到繼承,重寫牽扯到繼承。

上面都能構(gòu)成函數(shù)重載,調(diào)用結(jié)果如下:

- 函數(shù)重載的作用
比如一個(gè)“攻擊”函數(shù),攻擊方式有:使用刀子工具,使用槍攻擊,使用大棒攻擊。
如果寫成三個(gè)函數(shù),然后分別傳入不同的攻擊方式,就會感覺這三個(gè)函數(shù)很相似而且沒必要而且函數(shù)名不好記。
所以我們可以使用函數(shù)重載,重載后的三個(gè)函數(shù)的函數(shù)名相同,參數(shù)類型不同,這樣就可以解決上面的問題。
- 函數(shù)重載注意點(diǎn)
返回值類型與函數(shù)重載無關(guān),例如下面不構(gòu)成函數(shù)重載。

默認(rèn)參數(shù)值和函數(shù)重載一起使用產(chǎn)生二義性時(shí),編譯器并不會報(bào)錯(cuò)(在C++中會報(bào)錯(cuò))

可變參數(shù),省略參數(shù)標(biāo)簽、函數(shù)重載一起使用產(chǎn)生二義性時(shí),編譯器有可能會報(bào)錯(cuò)

五. 內(nèi)聯(lián)函數(shù)(Inline Function)
內(nèi)聯(lián)函數(shù)就是將函數(shù)調(diào)用展開成函數(shù)體。
如果開啟了編譯器優(yōu)化(Release模式默認(rèn)會開啟優(yōu)化),編譯器會自動將某些函數(shù)變成內(nèi)聯(lián)函數(shù)。
開啟編譯器優(yōu)化步驟如下:

哪些函數(shù)不會被自動內(nèi)聯(lián)?
函數(shù)體比較長
包含遞歸調(diào)用
包含動態(tài)派發(fā)
......
什么是動態(tài)派發(fā)?
動態(tài)派發(fā)類似于OC的多態(tài),就是父類指針指向子類對象,在編譯時(shí)并不知道要調(diào)用的是父類還是子類的方法,只有在運(yùn)行的時(shí)候才能知道實(shí)際調(diào)用哪個(gè)方法。
編譯器將某些函數(shù)變成內(nèi)聯(lián)函數(shù),是在編譯時(shí)期,但是在編譯時(shí)期無法確定調(diào)用哪個(gè)函數(shù),所以包含動態(tài)派發(fā)不會被自動內(nèi)聯(lián)。
- @inline

在Release模式下,編譯器已經(jīng)開啟優(yōu)化,會自動決定哪些函數(shù)需要內(nèi)聯(lián),因此沒必要使用@inline
六. 函數(shù)類型(Function Type)
每一個(gè)函數(shù)都是有類型的,函數(shù)類型由形式參數(shù)類型、返回值類型組成。
比如下面的函數(shù),函數(shù)類型分別是右邊的注釋:

注意:Void 其實(shí)就是 () 空元祖
- 函數(shù)類型作為函數(shù)參數(shù)

- 函數(shù)類型作為函數(shù)返回值

返回值是函數(shù)類型的函數(shù),叫做高階函數(shù)(Higher-Order Function)
- typealias
typealias用來給類型起別名,Swift中沒有Byte、Short、Long,我們可以通過起別名,定義一個(gè):

其中Int8一個(gè)字節(jié),Int16兩個(gè)字節(jié),Int64八個(gè)字節(jié)。
也可以給元祖起別名:

給函數(shù)類型起別名:

按照Swift標(biāo)準(zhǔn)庫的定義,Void就是空元組()
public typealias Void = ()
所以,當(dāng)函數(shù)返回值為空的時(shí)候,返回值可以什么都不寫,也可以寫Void,也可以寫()。
- 嵌套函數(shù)(Nested Function)
將函數(shù)定義在函數(shù)內(nèi)部

因?yàn)閚ext和previous函數(shù)只在forward函數(shù)里面使用到,所以可以把next和previous函數(shù)定義在forward函數(shù)里面。