函數的定義
func: 聲明這是一個函數
sum:函數名
(v1: Int, v2: Int): v1,v2是參數名, Int是參數類型
-> Int:返回值爲Int
sum(v1: 10, v2: 20):調用函數,調用函數時有參數名(v1, v2)必須帶着
形參默認是let,也只能是let
- 無返回值的函數
->()表示返回值爲空,()實際上是一個空元組
-------------------------------------------------------------------------------------------------------------------------------
隱式返回
如果整個函數體是一個單一表達式,那麼函數會隱式返回這個表達式
-------------------------------------------------------------------------------------------------------------------------------
返回元組:實現多返回值
-------------------------------------------------------------------------------------------------------------------------------
函數的文檔註釋
參考:https://swift.org/documentation/api-design-guidelines/
-------------------------------------------------------------------------------------------------------------------------------
參數標籤(Argument Label)
- 可以修改參數標籤
- 可以使用下劃線加空格_ 省略參數標籤
-------------------------------------------------------------------------------------------------------------------------------
默認參數值(Deafault Parater Value)
- 參數可以有默認值
- C++的默認參數有個限制:必須從右往左設置。由於Swift擁有參數標籤,所以並沒有此類限制
- 但是在省略參數標籤時需要特別注意:要傳入的沒有默認值的參數必須要設置參數標籤
-------------------------------------------------------------------------------------------------------------------------------
可變參數(Variadic Parameter)
Int...:表示一個或多個類型爲Int的參數
一個參數最多隻能有一個可變參數
緊跟在可變參數後面的參數不能省略參數標籤,否則會認爲是可變參數中的其中一個參數
-------------------------------------------------------------------------------------------------------------------------------
Swift自帶的print函數
-------------------------------------------------------------------------------------------------------------------------------
輸入輸出參數(In-Out Parameters)
注意:Swift屏蔽掉了一些東西,不能直接輸出地址,會報錯
Xcode進入彙編模式: DeBug --> Debug Workflow --> Always Show Disassembly
下圖:callq(表示這是一個函數方法) 0x100000f60表示函數地址
lea:彙編裏的地址傳遞指令, 表示把0x10da加rip表示的地址傳遞給rdi,用%開頭的表示寄存器,也就是說rdi裏存的是地址值
(%rdi)表示根據rdi存儲的地址值找到相應的存儲空間
mov指令是賦值指令。
普通參數傳遞和inout參數地址傳遞彙編代碼對比:
/*
這是普通參數傳遞,明顯有mov賦值指令
var number = 10
func test(_ num: Int) {
}
test(number)
0x100000f5e <+78>: movq -0x30(%rbp), %rdi
0x100000f62 <+82>: callq 0x100000f70 ; TestSwift.test(Swift.Int) -> () at main.swift:24
這是inout輸入輸出參數,沒有mov賦值指令,有lea地址指令
var number = 10
func test(_ num: inout Int) {
}
test(&number)
0x100000f47 <+55>: leaq 0x10ca(%rip), %rdi ; TestSwift.number : Swift.Int
0x100000f4e <+62>: callq 0x100000f70 ; TestSwift.test(inout Swift.Int) -> () at main.swift:24
*/
這裏說彙編是因爲要論證函數的輸入輸出參數(inout)是地址傳遞,並不是網上其他人說的先把參數傳給一箇中間值,然後把中間值當作返回值返回,然後賦值給外部參數,這裏涉及到彙編就不多說了。
- 可以用inout定義一個輸入輸出參數:可以在函數內部修改外部實參的值,比如交換兩個數的值
也可以用元組
- 可變參數不能標記爲inout
- inout參數不能有默認值
- inout參數的本質是地址傳遞(引用傳遞)
- inout參數只能傳入可以被多次賦值的(也就是需要用var,不能用let,更不能用40這種字面量)
- 特別注意,大部分是引用傳遞,但是當傳遞給inout參數的是計算屬性,有監聽器的屬性等內容,其本質並非引用傳遞
-------------------------------------------------------------------------------------------------------------------------------
函數重載
- 規則:
- 函數名相同
- 參數個數不同 || 參數類型不同 || 參數標籤不同
和第一個相比參數個數不同
和第一個相比參數類型不同
和第一個相比參數標籤不同
相應的輸出結果:
-------------------------------------------------------------------------------------------------------------------------------
函數重載注意點
- 返回值類型與函數重載無關,即使不一樣也會報錯
編譯器分不清該用哪個
- 默認參數值和函數重載一起使用產生二義性,但是編譯器不會報錯(在C++中會報錯)
默認使用第一個sum函數
- 可變參數、省略參數標籤、函數重載一起使用產生二義性時,編譯器有可能會報錯
-------------------------------------------------------------------------------------------------------------------------------
內聯函數(Inline Function)
- 如果開啓了編譯器優化(Release模式默認會開啓優化,Debug模式默認不開啓,需要手動調整),編譯器會自動將某些函數變爲內聯函數。
- 將函數調用展開成函數體(比如將function test {print("A")} 直接轉化成print("A"),消除了函數體的調用,節省了內存開銷)
- 哪些函數不會被內聯
- 函數體比較長
- 包含遞歸調用
- 包含動態派發
- @inline
在Release模式下,編譯器已經開啓優化,會自動決定哪些函數需要內聯 ,因此沒必要使用@inline
-------------------------------------------------------------------------------------------------------------------------------
函數類型(Function Type)
- 每個函數都是有類型的,函數類型由形式參數類型、返回值類型組成
函數類型爲: () -> Void 或 () -> ()
函數類型爲: (Int, Int) -> Int
通過函數類型定義變量,調用時不需要參數標籤
- 函數類型作爲函數參數
- 函數類型作爲函數返回值
其中forward(_ forward: Bool) -> (Int) ->Int 表示forward(_ forward: Bool)這個函數的返回值是一個函數類型,即(Int) ->Int,它表示一個參數爲Int,返回值也是Int類型的函數
返回值是函數類型的函數,叫做高階函數。
-------------------------------------------------------------------------------------------------------------------------------
typealias
- typealias用來給類型起別名
typealias給元組起別名
typealias給函數類型起別名IntFn,將IntFn作爲參數類型和返回值類型
-------------------------------------------------------------------------------------------------------------------------------
嵌套函數(Nested Function)
- 將函數定義在函數內部