iOS-Swift-函數(shù)

一. 函數(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ù)的文檔注釋

格式如下:

函數(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ù)標(biāo)簽

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

可以使用下劃線 _ 省略參數(shù)標(biāo)簽。

_省略參數(shù)標(biāo)簽

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

不推薦省略參數(shù)標(biāo)簽

2. 默認(rèn)參數(shù)值(Default Parameter Value)

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

默認(rèn)參數(shù)

C++的默認(rèn)參數(shù)值有個(gè)限制:必須從右往左設(shè)置。由于Swift擁有參數(shù)標(biāo)簽,因此并沒有此類限制。

3. 可變參數(shù)(Variadic Parameter)

一個(gè)函數(shù)最多只能有1個(gè)可變參數(shù)。

可變參數(shù)

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

不能省略
  • Swift自帶的print函數(shù)
print

可以發(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ù)

輸入輸出函數(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)簽不同

重載和重寫不一樣,重載牽扯不到繼承,重寫牽扯到繼承。

函數(shù)重載

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

函數(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ù)重載。

不構(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ò)

二義性報(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)化步驟如下:

編譯器優(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
@inline

在Release模式下,編譯器已經(jīng)開啟優(yōu)化,會自動決定哪些函數(shù)需要內(nèi)聯(lián),因此沒必要使用@inline

六. 函數(shù)類型(Function Type)

每一個(gè)函數(shù)都是有類型的,函數(shù)類型由形式參數(shù)類型、返回值類型組成。

比如下面的函數(shù),函數(shù)類型分別是右邊的注釋:

函數(shù)類型

注意:Void 其實(shí)就是 () 空元祖

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

返回值是函數(shù)類型的函數(shù),叫做高階函數(shù)(Higher-Order Function)

  • typealias

typealias用來給類型起別名,Swift中沒有Byte、Short、Long,我們可以通過起別名,定義一個(gè):

Byte、Short、Long

其中Int8一個(gè)字節(jié),Int16兩個(gè)字節(jié),Int64八個(gè)字節(jié)。

也可以給元祖起別名:

給元祖起別名

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

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

按照Swift標(biāo)準(zhǔn)庫的定義,Void就是空元組()

public typealias Void = ()

所以,當(dāng)函數(shù)返回值為空的時(shí)候,返回值可以什么都不寫,也可以寫Void,也可以寫()。

  • 嵌套函數(shù)(Nested Function)

將函數(shù)定義在函數(shù)內(nèi)部

嵌套函數(shù)

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容