swift 函數(shù)類型+高階函數(shù)

Swift 函數(shù)用來(lái)完成特定任務(wù)的獨(dú)立的代碼塊。
Swift使用一個(gè)統(tǒng)一的語(yǔ)法來(lái)表示簡(jiǎn)單的C語(yǔ)言風(fēng)格的函數(shù)到復(fù)雜的Objective-C語(yǔ)言風(fēng)格的方法。
函數(shù)聲明: 告訴編譯器函數(shù)的名字亚兄,返回類型及參數(shù)。
函數(shù)定義: 提供了函數(shù)的實(shí)體隔躲。

函數(shù)

Swift 函數(shù)包含了參數(shù)類型及返回值類型:

函數(shù)定義

函數(shù)的參數(shù)傳遞的順序必須與參數(shù)列表相同。
函數(shù)的實(shí)參傳遞的順序必須與形參列表相同,-> 后定義函數(shù)的返回值類型。

//語(yǔ)法
func funcname(形參) -> returntype
{
   Statement1
   Statement2
   ……
   Statement N
   return parameters
}
//例子
func runoob(site: String) -> String {
    return (site)
}

函數(shù)參數(shù)+不帶參數(shù)函數(shù)+沒(méi)有返回值函數(shù)

函數(shù)可以接受一個(gè)或者多個(gè)參數(shù)量愧,這些參數(shù)被包含在函數(shù)的括號(hào)之中,以逗號(hào)分隔帅矗。

func runoob(name: String, site: String) -> String {
    return name + site
}
func sitename() -> String {
    return "123"
}

元組作為函數(shù)返回值

函數(shù)返回值類型可以是字符串偎肃,整型,浮點(diǎn)型等浑此。
元組與數(shù)組類似累颂,不同的是,\color{#FF00FF}{元組中的元素可以是任意類型凛俱,使用的是圓括號(hào)}紊馏。
你可以用元組(tuple)類型讓多個(gè)值作為一個(gè)復(fù)合值從函數(shù)中返回。
下面的這個(gè)例子中最冰,定義了一個(gè)名為minMax(_:)的函數(shù)瘦棋,作用是在一個(gè)Int數(shù)組中找出最小值與最大值。

func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}
let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("最小值為 \(bounds.min) 暖哨,最大值為 \(bounds.max)")
//最小值為 -6 赌朋,最大值為 109

如果你不確定返回的元組一定不為nil凰狞,那么你可以返回一個(gè)可選的元組類型。
你可以通過(guò)在元組類型的右括號(hào)后放置一個(gè)問(wèn)號(hào)來(lái)定義一個(gè)可選元組沛慢,例如(Int, Int)?或(String, Int, Bool)?

注意
可選元組類型如(Int, Int)?與元組包含可選類型如(Int?, Int?)是不同的.可選的元組類型赡若,整個(gè)元組是可選的,而不只是元組中的每個(gè)元素值团甲。

func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}
if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
    print("最小值為 \(bounds.min)逾冬,最大值為 \(bounds.max)")
}
//沒(méi)有返回值函數(shù)
func runoob(site: String) {
    print(site)
}

函數(shù)參數(shù)名稱

函數(shù)參數(shù)都有一個(gè)外部參數(shù)名和一個(gè)局部參數(shù)名。

局部參數(shù)名

局部參數(shù)名在函數(shù)的實(shí)現(xiàn)內(nèi)部使用躺苦。

//以下實(shí)例中 number 為局部參數(shù)名身腻,只能在函數(shù)體內(nèi)使用。
func sample(number: Int) {
   println(number)
}
外部參數(shù)名

你可以在局部參數(shù)名前指定外部參數(shù)名匹厘,中間以空格分隔嘀趟,外部參數(shù)名用于在函數(shù)調(diào)用時(shí)傳遞給函數(shù)的參數(shù)。
如下你可以定義以下兩個(gè)函數(shù)參數(shù)名并調(diào)用它:

func pow(firstArg a: Int, secondArg b: Int) -> Int {
   var res = a
   for _ in 1..<b {
      res = res * a
   }
   print(res)
   return res
}
pow(firstArg:5, secondArg:3)
//125

注意
如果你提供了外部參數(shù)名愈诚,那么函數(shù)在被調(diào)用時(shí)她按,必須使用外部參數(shù)名。

可變參數(shù)

可變參數(shù)可以接受零個(gè)或多個(gè)值炕柔。函數(shù)調(diào)用時(shí)酌泰,你可以用可變參數(shù)來(lái)指定函數(shù)參數(shù),其數(shù)量是不確定的匕累。
可變參數(shù)通過(guò)在變量類型名后面加入(...)的方式來(lái)定義陵刹。

func vari<N>(members: N...){
    for i in members {
        print(i)
    }
}
vari(members: 4,3,5)
vari(members: 4.5, 3.1, 5.6)
vari(members: "Google", "Baidu", "Runoob")

常量,變量及 I/O 參數(shù)

一般默認(rèn)在函數(shù)中定義的參數(shù)都是常量參數(shù)哩罪,也就是這個(gè)參數(shù)你只可以查詢使用授霸,不能改變它的值。
如果想要聲明一個(gè)變量參數(shù)际插,可以在參數(shù)定義前加\color{#FF00FF}{inout} 關(guān)鍵字碘耳,這樣就可以改變這個(gè)參數(shù)的值了。

func  getName(_ name: inout String).........

此時(shí)這個(gè) name 值可以在函數(shù)中改變框弛。
一般默認(rèn)的參數(shù)傳遞都是傳值調(diào)用的辛辨,而不是傳引用。所以傳入的參數(shù)在函數(shù)內(nèi)改變瑟枫,并不影響原來(lái)的那個(gè)參數(shù)斗搞。傳入的只是這個(gè)參數(shù)的副本。
當(dāng)傳入的參數(shù)作為輸入輸出參數(shù)時(shí)慷妙,需要在參數(shù)名前加 & 符僻焚,表示這個(gè)值可以被函數(shù)修改。 以下是實(shí)例:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}


var x = 1
var y = 5
swapTwoInts(&x, &y)
print("x 現(xiàn)在的值 \(x), y 現(xiàn)在的值 \(y)")
//x 現(xiàn)在的值 5, y 現(xiàn)在的值 1

函數(shù)使用

在 Swift 中膝擂,使用函數(shù)類型就像使用其他類型一樣虑啤。例如隙弛,你可以定義一個(gè)類型為函數(shù)的常量或變量,并將適當(dāng)?shù)暮瘮?shù)賦值給它:

func sum(a: Int, b: Int) -> Int {
   return a + b
}
var addition: (Int, Int) -> Int = sum
print("輸出結(jié)果: \(addition(40, 89))")
//輸出結(jié)果: 129

解析:
"定義一個(gè)叫做 addition 的變量狞山,參數(shù)與返回值類型均是 Int 全闷,并讓這個(gè)新變量指向 sum 函數(shù)"。
sum 和 addition 有同樣的類型萍启,所以以上操作是合法的总珠。
現(xiàn)在,你可以用 addition 來(lái)調(diào)用被賦值的函數(shù)了:

函數(shù)類型作為參數(shù)類型勘纯、函數(shù)類型作為返回類型

func sum(a: Int, b: Int) -> Int {
    return a + b
}
func another(addition: (Int, Int) -> Int, a: Int, b: Int) {
    print("輸出結(jié)果: \(addition(a, b))")
}
another(addition: sum, a: 10, b: 20)
//輸出結(jié)果: 30

函數(shù)嵌套

函數(shù)嵌套指的是函數(shù)內(nèi)定義一個(gè)新的函數(shù)局服,外部的函數(shù)可以調(diào)用函數(shù)內(nèi)定義的函數(shù)。

func calcDecrement(forDecrement total: Int) -> () -> Int {
   var overallDecrement = 0
   func decrementer() -> Int {
      overallDecrement -= total
      return overallDecrement
   }
   return decrementer
}
let decrem = calcDecrement(forDecrement: 30)
print(decrem())
//-30

高階函數(shù)

1.map
對(duì)于原始集合里的每一個(gè)元素屡律,以一個(gè)變換后的元素替換之形成一個(gè)新的集合

let numbers = [1, 2, 4, 5, 10] 
print(numbers.map{ $0 * 10 })  
//打印: [10, 20, 40, 50, 100]
print(numbers.map({ (value) -> String in
    return String(value)
}))
// 變成字符串?dāng)?shù)組
// Swift中閉包是函數(shù)的唯一參數(shù)或是其最后一個(gè)參數(shù)時(shí)腌逢,map的()可以被省略
numbers.map { (value) -> String in
    return String(value)
}
numbers.map { value in String(value)}
print(numbers.map{ String($0)})   
//直接通過(guò)$0,$1,$2來(lái)順序調(diào)用閉包的參數(shù)
//以上四種方法都是等價(jià)的

還有一種應(yīng)用場(chǎng)景,就是解析可選類型的時(shí)候超埋,map和flatMap函數(shù)會(huì)讓你的代碼更加優(yōu)雅。

舉個(gè)例子佳鳖,當(dāng)解析并判斷可選類型的時(shí)候霍殴,你可能會(huì)經(jīng)過(guò)一堆if或者guard判斷,如下所示:

func loadURL(url: URL) {
    print(url.absoluteString)
}

let urlStr: String? = "https://github.com/wangyanchang21"
guard let siteStr = urlStr else {
    assert(false)
}
guard let url = URL(string: siteStr) else {
    assert(false)
}
loadURL(url: url)

如果使用map和flatMap函數(shù)的話系吩,就會(huì)有十分優(yōu)雅的感覺(jué)来庭。

// 這行優(yōu)雅的代碼代替上面的代碼
urlStr.flatMap(URL.init).map(loadURL)

但有一點(diǎn)需要注意,這里 map替換 flatMap會(huì)報(bào)錯(cuò), 原因在于 flatMap閉包可以返回 nil, 而 map閉包不可以穿挨。就如下面的代碼編譯不會(huì)通過(guò):

// compile error
// urlStr.map(URL.init).map(loadURL)

2.flatMap
對(duì)于元素是集合的集合月弛,可以得到單級(jí)的集合

let arrayNumbers = [[1, 2, 3], [4, 5, 6], [7, 8, 9 ]]
print(arrayNumbers.flatMap{ $0 }) 
//打印: [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(arrayNumbers.flatMap{ $0.map{ $0 * 10} })  
//打印: [10, 20, 30, 40, 50, 60, 70, 80, 90]

但是不是所有的首層元素都可以降維得到單級(jí)集合
1.第一種情況,解析首層元素科盛,若有nil則過(guò)濾帽衙,就不會(huì)降維

let optLatticeNumbers = [[1, Optional(2), 3], [3, nil, 5], nil]
// 解析首層元素, 若有nil則過(guò)濾, 就不會(huì)降維
let flatMapArr2 = optLatticeNumbers.flatMap { $0 }
// [[1, 2, 3], [3, nil, 5]]

2.解析首層元素,若沒(méi)有nil贞绵,則會(huì)降維

let latticeNumbers = [[1, Optional(2), 3], [3, nil, 5]]
// 解析首層元素, 若沒(méi)有nil, 則會(huì)降維
let flatMapArr = latticeNumbers.flatMap { $0 }
// [1, 2, 3, 3, nil, 5]

為了將過(guò)濾nil和降維兩個(gè)功能于區(qū)分開(kāi)厉萝,swift4.1開(kāi)始,就只保留了降維的flatMap函數(shù)榨崩,并棄用了過(guò)濾nil的flatMap函數(shù)谴垫,又用開(kāi)放的新函數(shù)compactMap來(lái)替代棄用的函數(shù)。
所以母蛛,當(dāng)需要過(guò)濾nil的時(shí)候翩剪,請(qǐng)使用compactMap函數(shù);當(dāng)需要進(jìn)行降維時(shí)彩郊,請(qǐng)使用flatMap函數(shù)前弯。這也就是flatMap和compactMap之間的區(qū)別蚪缀。

3.compactMap
過(guò)濾空值

let names: [String?] = ["zhangsan", nil, "lisi", "wangwu", nil,  "zhaoliu"]
print(names.count)    
// 6
print(names.map{ $0?.count })
//[Optional(8), nil, Optional(4), Optional(6), nil, Optional(7)]
print(names.compactMap{ $0 })   
//打印: ["zhangsan", "lisi", "wangwu", "zhaoliu"],過(guò)濾了空值
print(names.compactMap{ $0?.count })   
//打印不是空值的字符串的個(gè)數(shù)
// [8, 4, 6, 7]

注意??:可選類型的 map作用是對(duì)可選類型進(jìn)行解包操作,若有值則進(jìn)入閉包博杖,并返回一個(gè) Optional類型椿胯;若為nil,則直接返回當(dāng)前可選類型的nil剃根。

4.filter
對(duì)于原始集合里的每一個(gè)元素哩盲,通過(guò)判定來(lái)將其丟棄或者放進(jìn)新集合

let numbers = [1, 2, 4, 5, 10]
print(numbers.filter{$0 > 4})
//打印: [5, 10]

5.reduce
對(duì)于原始集合里的每一個(gè)元素,作用于當(dāng)前累積的結(jié)果上

let numbers = [1, 2, 4, 5, 10]
print(numbers.reduce(100, { $0 + $1 }))   
//打印: 122
// 100 是初始值, 將所有數(shù)相加: 100 + 1 + 2 + 4 + 5 + 10 = 122

6.sort函數(shù)
對(duì)原集合進(jìn)行給定條件排序狈醉。
無(wú)返回值廉油,直接修改原集合,所以這個(gè)集合應(yīng)該是可變類型的苗傅。

var numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
numbers.sort { a, b in
    return a < b
}       
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

另外抒线,系統(tǒng)還定義了一個(gè)sort()函數(shù),即對(duì)集合進(jìn)行升序排序的函數(shù)渣慕。但這個(gè)函數(shù)并不是上面函數(shù)不傳入缺省值的情況嘶炭,而是另外一個(gè)函數(shù)。

var numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
numbers.sort()      
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

7.sorted函數(shù)
sorted函數(shù)與sort函數(shù)對(duì)應(yīng)逊桦。
將集合進(jìn)行給定條件排序眨猎,返回一個(gè)新的集合,不修改原集合强经。

let sortedArr = numbers.sorted { a, b in
    return a > b
}
// [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

// sorted()函數(shù)
let sortedArr2 = numbers.sorted()
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// 閉包簡(jiǎn)寫(xiě)
let sortedArr3 = sortedArr2.sorted(by: >)
// [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

8.prefix函數(shù)
正向取滿足條件的元素睡陪,進(jìn)行新集合創(chuàng)建。一旦出現(xiàn)不滿足條件的元素匿情,則跳出循環(huán)兰迫,不再執(zhí)行。

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
let prefixArr = numbers.prefix { $0 < 10 }
// [7, 6]

prefix相關(guān)函數(shù):
\color{#FF00FF}{upTo}: 正向取元素創(chuàng)建數(shù)組, 包含小于指定index的元素

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
let prefixUpToArr = numbers.prefix(upTo: 5)
// [7, 6, 10, 9, 8]

\color{#FF00FF}{through}:正向取元素創(chuàng)建數(shù)組, 包含小于等于指定index的元素

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
let prefixThroughArr = numbers.prefix(through: 2)
// [7, 6, 10]

\color{#FF00FF}{maxLength}: 正向取元素創(chuàng)建數(shù)組, 包含指定的元素個(gè)數(shù)

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
let prefixMaxLengthArr = numbers.prefix(6)
// [7, 6, 10, 9, 8, 1]

9.drop函數(shù)
與prefix函數(shù)對(duì)應(yīng)炬称。正向跳過(guò)滿足條件的元素汁果,進(jìn)行新集合創(chuàng)建。

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
let dropArr = numbers.drop { $0 < 10 }
// [10, 9, 8, 1, 2, 3, 4, 5]

drop相關(guān)函數(shù):
\color{#FF00FF}{dropFirst}: 正向跳過(guò)元素創(chuàng)建數(shù)組, 跳過(guò)指定元素個(gè)數(shù), 缺省值為1

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
let dropFirstArr = numbers.dropFirst(3)
// [9, 8, 1, 2, 3, 4, 5]

\color{#FF00FF}{dropLast}:返向跳過(guò)元素創(chuàng)建數(shù)組, 跳過(guò)指定元素個(gè)數(shù), 缺省值為1

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
let dropLastArr = numbers.dropLast(5)
// [7, 6, 10, 9, 8]

10.first函數(shù)
正向找出第一個(gè)滿足條件的元素转砖。

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
let first = numbers.first { $0 < 7 }
// 6

11.last函數(shù)
與first函數(shù)對(duì)應(yīng)须鼎。反向找出第一個(gè)滿足條件的元素。

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
let last = numbers.last { $0 > 5 }
// 8

12.firstIndex函數(shù)
正向找出第一個(gè)滿足條件的元素下標(biāo)府蔗。

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
let firstIndex = numbers.firstIndex { $0 < 7 }
// 1

13.lastIndex函數(shù)
反向找出第一個(gè)滿足條件的元素下標(biāo)晋控。

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
let lastIndex = numbers.lastIndex { $0 > 5 }
// 4

14.partition函數(shù)()(不常用)
按照條件進(jìn)行重新排序,不滿足條件的元素在集合前半部分姓赤,滿足條件的元素后半部分赡译,但不是完整的升序或者降序排列。
返回值為排序完成后集合中第一個(gè)滿足條件的元素下標(biāo)不铆。

var partitionNumbers = [20, 50, 30, 10, 40, 20, 60]
let pIndex = partitionNumbers.partition { $0 > 30 }
// partitionNumbers = [20, 20, 30, 10, 40, 50, 60]
// pIndex = 4

15.min函數(shù)
按條件排序后取最小元素蝌焚。

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
let min = numbers.min { $0 % 5 < $1 % 5 }
// 10

min()函數(shù)裹唆,自然升序取最小。

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
let minDefault = numbers.min()
// 1

16.max函數(shù)
按條件排序后取最大元素只洒。

let maxDictionary = ["aKey": 33, "bKey": 66, "cKey": 99]
let max = maxDictionary.max { $0.value < $1.value }
// (key "cKey", value 99)

max()函數(shù)许帐,自然升序取最大。

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
let maxDefault = numbers.max()
// 10

17.removeAll函數(shù)
移除原集合中所有滿足條件的元素毕谴。
無(wú)返回值成畦,直接修改原集合,所以這個(gè)集合應(yīng)該是可變類型的涝开。

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
var removeArr = numbers
removeArr.removeAll { $0 > 6 }
// [6, 1, 2, 3, 4, 5]

18.集合遍歷

let numbers = [7, 6, 10, 9, 8, 1, 2, 3, 4, 5]
//forEach函數(shù):
numbers.forEach { num in
    print(num)
}
//for-in函數(shù):
for num in numbers where num < 5 {
    print(num)
}
//與enumerated()函數(shù)配合使用:
for (index, num) in numbers.enumerated() {
    print("\(index)-\(num)")
}

19.shuffled函數(shù)
shuffled函數(shù)循帐,打亂集合中元素的的順序。

let ascendingNumbers = 0...9
let shuffledArr = ascendingNumbers.shuffled()
// [3, 9, 2, 6, 4, 5, 0, 1, 7, 8]

20.split和joined函數(shù)
split函數(shù)舀武,字符串的函數(shù)拄养,按條件分割字符串,為子字符串創(chuàng)建集合银舱。與Objective-C中的componentsSeparatedByString:方法類似瘪匿。

let line = "123Hi!123I'm123a123coder.123"
let splitArr = line.split { $0.isNumber }
// ["Hi!", "I'm", "a", "coder."]

// 也可指定字符
let splitArr2 = line.split(separator: "1")
// ["23Hi!", "23I'm", "23a", "23coder.", "23"]

joined函數(shù),數(shù)組元素連接指定字符拼接成一個(gè)字符串寻馏。與Objective-C中的componentsJoinedByString:方法類似柿顶。

let joined = splitArr.joined(separator: "_")
// "Hi!_I'm_a_coder."
// 也可以只傳入字符
let joined2 = splitArr2.joined(separator: "#")
// "23Hi!#23I'm#23a#23coder.#23"

21.zip函數(shù)
將兩個(gè)數(shù)組合并為一個(gè)元組組成的數(shù)組。

let titles = ["aaa", "bbb", "ccc"]
let numbers = [111, 222, 333]
let zipA = zip(titles, numbers)
for (title, num) in zipA {
    print("\(title)-\(num)")
}
//aaa-111
//bbb-222
//ccc-333

內(nèi)聯(lián)函數(shù)(Inline Function)

如果開(kāi)啟了編譯器優(yōu)化操软,編譯器會(huì)自動(dòng)將某些函數(shù)變成內(nèi)聯(lián)函數(shù)(將函數(shù)調(diào)用展開(kāi)成函數(shù)體),我們可以看到 Release 模式默認(rèn)開(kāi)啟優(yōu)化宪祥,并且是按照速度去優(yōu)化:



比如我們有一個(gè)函數(shù)聂薪,那么一旦調(diào)用 test() 這個(gè)函數(shù),系統(tǒng)就會(huì)為這個(gè)函數(shù)分配椈妊颍空間藏澳,并且在棧空間進(jìn)行分配局部變量的操作耀找,函數(shù)執(zhí)行完之后會(huì)對(duì)函數(shù)椣栌疲空間進(jìn)行回收:

func test() {
    print("test")
}
test()

我們思考一下,test() 這個(gè)函數(shù)里面的代碼非常少野芒,僅僅做一件事情打印蓄愁,那么我們直接把 test() 函數(shù)里這行代碼拿出來(lái),這樣性能不就更好嗎狞悲?其實(shí)內(nèi)聯(lián)做的操作就是這樣撮抓,將函數(shù)調(diào)用展開(kāi)成函數(shù)體代碼,這樣就減少了函數(shù)的調(diào)用開(kāi)銷摇锋,不必再開(kāi)辟回收函數(shù)的椀ふ空間了站超。展開(kāi)后代碼如下:

print("test")

接下來(lái)我們運(yùn)行看一下匯編代碼,首先在第76行代碼打一個(gè)斷點(diǎn)乖酬,然后打開(kāi)顯示反匯編選項(xiàng)(Debug -> Debug Workflow -> Always show Disassembly):



1死相、首先編譯器未開(kāi)啟優(yōu)化的情況:


 0x10b294a65 <+5989>: callq  0x10b2956b0               ; test() -> () at ViewController.swift:73

我們發(fā)現(xiàn)有一個(gè) callq 0x10b2956b0 的操作,這句匯編代碼的意思就是調(diào)用 test() 函數(shù)咬像,所以未開(kāi)啟優(yōu)化的情況下沒(méi)有進(jìn)行內(nèi)聯(lián)操作算撮。

2、然后我們打開(kāi)編譯器優(yōu)化選項(xiàng):



我們?cè)?test() 函數(shù)調(diào)用的地方第76行設(shè)置斷點(diǎn)施掏,然后運(yùn)行钮惠,我們發(fā)現(xiàn) test() 函數(shù)沒(méi)有執(zhí)行,但是 “test” 字符串被打印出來(lái)了:


然后我們把斷點(diǎn)設(shè)置到第 74 行七芭,運(yùn)行查看匯編代碼


0x10ae951cf <+1871>: callq  0x10ae96e1a               ; symbol stub for: Swift.print(_: Any..., separator: Swift.String, terminator: Swift.String) -> ()

我們看匯編可以發(fā)現(xiàn)素挽,print(“test”)代碼被直接放到 main 函數(shù)當(dāng)中,也就是編譯器幫我們做了內(nèi)聯(lián)操作狸驳。

但是有一些函數(shù)是不會(huì)被內(nèi)聯(lián):
1预明、函數(shù)體比較長(zhǎng)的函數(shù)不會(huì)被內(nèi)聯(lián)(如果函數(shù)體比較長(zhǎng),函數(shù)被調(diào)用次數(shù)也非常多耙箍,進(jìn)行內(nèi)聯(lián)操作生成的匯編代碼會(huì)非常多撰糠,也就是機(jī)器碼會(huì)變多,最終代碼體積變大安裝包變大)辩昆;
2阅酪、遞歸調(diào)用不會(huì)被內(nèi)聯(lián);
3汁针、包含動(dòng)態(tài)派發(fā)(類似Oc動(dòng)態(tài)綁定)的函數(shù)不會(huì)被內(nèi)聯(lián)术辐;
我們也可以使用@online手動(dòng)關(guān)閉/開(kāi)啟內(nèi)聯(lián)優(yōu)化的:
1、下面的函數(shù)永遠(yuǎn)不會(huì)被內(nèi)聯(lián)(即使開(kāi)啟了編譯器優(yōu)化):

@inline(never) func test() {
    print("test")
}

2施无、開(kāi)啟編譯器優(yōu)化后辉词,即使代碼很長(zhǎng)也會(huì)被內(nèi)聯(lián)(遞歸調(diào)用,動(dòng)態(tài)派發(fā)的函數(shù)除外猾骡;在 release 模式下瑞躺,編譯器已經(jīng)開(kāi)啟優(yōu)化,會(huì)自動(dòng)決定哪些函數(shù)需要內(nèi)聯(lián)兴想,因此沒(méi)必要使用 @inline):

@inline(__always) func test() {
    print("test")
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末幢哨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子襟企,更是在濱河造成了極大的恐慌嘱么,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異曼振,居然都是意外死亡几迄,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門冰评,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)映胁,“玉大人,你說(shuō)我怎么就攤上這事甲雅〗馑铮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵抛人,是天一觀的道長(zhǎng)弛姜。 經(jīng)常有香客問(wèn)我,道長(zhǎng)妖枚,這世上最難降的妖魔是什么廷臼? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮绝页,結(jié)果婚禮上荠商,老公的妹妹穿的比我還像新娘。我一直安慰自己续誉,他們只是感情好莱没,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著酷鸦,像睡著了一般饰躲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上臼隔,一...
    開(kāi)封第一講書(shū)人閱讀 48,954評(píng)論 1 283
  • 那天属铁,我揣著相機(jī)與錄音,去河邊找鬼躬翁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛盯拱,可吹牛的內(nèi)容都是我干的盒发。 我是一名探鬼主播,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼狡逢,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼宁舰!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起奢浑,我...
    開(kāi)封第一講書(shū)人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蛮艰,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后雀彼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體壤蚜,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡即寡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了袜刷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片聪富。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖著蟹,靈堂內(nèi)的尸體忽然破棺而出墩蔓,到底是詐尸還是另有隱情,我是刑警寧澤萧豆,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布奸披,位于F島的核電站,受9級(jí)特大地震影響涮雷,放射性物質(zhì)發(fā)生泄漏阵面。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一份殿、第九天 我趴在偏房一處隱蔽的房頂上張望膜钓。 院中可真熱鬧,春花似錦卿嘲、人聲如沸颂斜。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沃疮。三九已至,卻和暖如春梅肤,著一層夾襖步出監(jiān)牢的瞬間司蔬,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工姨蝴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留俊啼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓左医,卻偏偏與公主長(zhǎng)得像授帕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浮梢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容