Swift學(xué)習(xí)筆記(三)--函數(shù)與閉包

函數(shù) (Functions)

  1. 函數(shù)的定義方法和ObjC差別很大, 第一次看起來會(huì)比較奇怪, 話說ObjC中[]這種方法調(diào)用方式都看過來了, 還有別的能難倒我們嗎? 直接以例子來看吧:
func sayHello(personName: String) -> String {
  let greeting = "Hello, " + personName + "!"
  return greeting
}
// 接受一個(gè)String的參數(shù), 返回一個(gè)String
//如果不需要參數(shù)不返回?cái)?shù)據(jù)則是:
func sayHello() -> (){ // 當(dāng)然也可以省略后面的 ->() 
  print("Hello!")
}

  1. 函數(shù)返回多個(gè)值
    當(dāng)然是利用強(qiáng)大的元組了,
func minMax(array: [Int]) -> (min: Int, max: Int) {
    // find min value and max value in array
   return (min, max)
}
// 調(diào)用
let bounds = minMax([8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
  1. 返回Optionals value
func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    // find min value and max value in array
    return (min, max)
}
// 調(diào)用
if let bounds = minMax([8, -6, 2, 109, 3, 71]) {
    print("min is \(bounds.min) and max is \(bounds.max)")
}
  1. 函數(shù)參數(shù)名:
    前面函數(shù)可以看到, 如果只有一個(gè)參數(shù), 名字是會(huì)被省略的, 具體原因我們可以從蘋果自己的命名看出來, Array里面有個(gè)joinWithSeparator(separator: String)方法, 可以看出, 蘋果希望第一個(gè)參數(shù)名是包含在函數(shù)名里面的, 如果不這樣做的話, 調(diào)用的時(shí)候大概是這樣的:
    joinWith(separator:"")
    比較下:
    joinWithSeparator("
    ")
    我個(gè)人是傾向于官方的版本, 因?yàn)檫@樣一來函數(shù)重名的概率就更小了, 在找方法的時(shí)候也更迅速, 同時(shí)用模糊匹配的插件也會(huì)更快. 但是可能又有人覺得這樣感覺不一致, 看大家的喜好吧

上面都是單參數(shù)的例子, 舉個(gè)多參數(shù)的例子:

func someFunction(firstParameterName: Int, secondParameterName: Int) {

}
someFunction(1, secondParameterName: 2)
// 第一個(gè)參數(shù)自動(dòng)隱藏, 如果一定要顯示出來就多寫一個(gè)標(biāo)簽
func otherFunction(firstParameterName  firstParameterName: Int, secondParameterName: Int) {

}
otherFunction(firstParameterName: 1, secondParameterName: 2)

// 同樣的 要是不想標(biāo)簽和變量名一樣 也可以自己指定
func sayHello(to person: String, and anotherPerson: String) -> String {
    return "Hello \(person) and \(anotherPerson)!"
}
print(sayHello(to: "Bill", and: "Ted"))

// 如果你想像C語言那么簡潔, 不想要參數(shù)標(biāo)簽, 那么可以用下劃線來隱藏掉
func addTwoNumber(a: Int, _ b: Int) {

}
addTwoNumber(1, 2)

5 默認(rèn)參數(shù)值:
不是什么新鮮玩意, 但是卻是有用的東西, 在C++里面早已經(jīng)有了, 但是ObjC里面因?yàn)闆]有所以在暴露一些接口的時(shí)候非常蛋疼.

func someFunction(parameterWithDefault: Int = 12) {
    print(parameterWithDefault)
}
someFunction(6) // 6
someFunction() // 12

6 不限參數(shù)
這個(gè)東西看起來比較高大上, 實(shí)際上原理應(yīng)該是比較簡單的, 先看例子吧:

func arithmeticMean(numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5) // 返回3.0
arithmeticMean(3, 8.25, 18.75) //返回10.0
// 因?yàn)轭愋褪且粯拥? 所以原理就是編譯器直接把參數(shù)替換成為[Double]類型, 然后在調(diào)用處自己組裝一個(gè)數(shù)組出來即可

7 常量參數(shù)和變量參數(shù)
函數(shù)入?yún)⒛J(rèn)是常量, 不允許修改的, 但是有時(shí)候不想重新定義一個(gè)變量出來, 就想要直接用這個(gè)變量就好了, 那么就加上一個(gè)在參數(shù)前面加上一個(gè)var即可, 例如:

func modifyParamFunction(var a: Int, b: Int) ->Int{
    a = b
    b = a // error
    return a
}

8 修改參數(shù)本身
在C和ObjC語言中, 修改參數(shù)本身的值是需要傳遞地址的, 在Swift里面也一樣, 不過因?yàn)闆]有指針運(yùn)算符, 所以需要顯式地在參數(shù)那里加一個(gè)inout, 例如:

func swapTwoInts(inout a: Int, inout _ b: Int) {
    let t = a
    a = b
    b = t
}
var a = 1, b = 2
swapTwoInts(&a, &b)  // &去掉會(huì)報(bào)錯(cuò)

9 函數(shù)類型
在Swift中, 函數(shù)本身也是一個(gè)變量, 自然就會(huì)有類型,例如,
上面sayHello這個(gè)函數(shù), 類型就是 ()->(), 我們可以把它賦值給一個(gè)變量:

var sayHi: (String)->(String) = sayHello
sayHi("Ryan") // 調(diào)用之

10 函數(shù)作為參數(shù)傳入
ObjC里面也有差不多的用法, 既然函數(shù)是一個(gè)對象, 且有類型, 那么直接把這個(gè)對象傳入即可, 例如:

func addTwoInts(a: Int, _ b: Int) -> Int {
    return a + b
}
func multiplyTwoInts(a: Int, _ b: Int) -> Int {
    return a * b
}
func printMathResult(mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)

11 函數(shù)作為返回值
與上面一樣的道理, 直接看例子:

func stepForward(input: Int) -> Int {
    return input + 1
}
func stepBackward(input: Int) -> Int {
    return input - 1
}
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
    return backwards ? stepBackward : stepForward
}
var currentValue = 3
let moveNearerToZero = chooseStepFunction(currentValue > 0)

while currentValue != 0 {
    currentValue = moveNearerToZero(currentValue)
}

12 嵌套函數(shù)
也就是函數(shù)里面還可以聲明并實(shí)現(xiàn)函數(shù), 只是這樣一來就無法在外部訪問了, 可以在一些函數(shù)里面寫一些簡單的輔助功能, 同時(shí)這些功能又沒有需要暴露的情況, 這樣可以很好地保持其封裝性, 如上面的例子可以寫成:

func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    return backwards ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}

函數(shù)大概就這么多, 只要語法看習(xí)慣了, 其實(shí)也沒什么太多的區(qū)別, 在類中實(shí)例方法也是一樣的定義, 類方法在func前面加個(gè)class罷了...
同樣, 需要看更多細(xì)節(jié)的, 查看官方文檔

閉包(Closure)

到了Swift讓我驚艷的部分了, 閉包和block的作用基本是相似的, 只是比起block來, 蘋果把閉包做的更加簡潔, 閉包的內(nèi)容很多, 但是如果有block的基礎(chǔ)的話, 應(yīng)該不會(huì)難懂.

  1. 閉包表達(dá)式(Closure Expressions)
    我們之前提到過一次閉包, 在數(shù)組排序的時(shí)候, 在數(shù)組的sort()函數(shù)中, 我們可以傳入函數(shù), 也可以傳入閉包(所以從某種角度來說, 這兩個(gè)東西就是一樣的)
func backwards(s1: String, _ s2: String) -> Bool {
  return s1 > s2
}
var reversed = names.sort(backwards)
// reversed 等于 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]

如果用閉包來寫就是這樣的:

reversed = names.sort({ (s1: String, s2: String) -> Bool in
  return s1 > s2
})
// 兇殘的蘋果對閉包的表達(dá)式進(jìn)行了一輪又一輪的簡化, 直達(dá)之前提過的最簡表達(dá)式
// 第一輪:
reversed = names.sort( { (s1: String, s2: String) -> Bool in return s1 > s2 } ) // 寫成一行業(yè)不算什么
// 第二輪:
reversed = names.sort( { s1, s2 in return s1 > s2 } )// 根據(jù)類型推導(dǎo)可以省略類型
// 第三輪:
reversed = names.sort( { s1, s2 in s1 > s2 } ) // 排序需要一個(gè)Bool值, 那么s1>s2這個(gè)表達(dá)式符合需求 省略return
// 第四輪:
reversed = names.sort( { $0 > $1 } ) // 知道會(huì)有2個(gè)參數(shù), 入?yún)⒌膶懛饬?// 第五輪:
reversed = names.sort(>) // 完全交給編譯器來推導(dǎo)...

總之, 上面這些寫法對于這種簡單一點(diǎn)的閉包來說, 還是很好用的, 個(gè)人建議到一般到第三輪, 很簡單到第四輪就差不多了

  1. 尾隨閉包(Trailing Closures)
    如果一個(gè)函數(shù)需要一個(gè)閉包作為參數(shù), 那么可以把閉包寫在函數(shù)調(diào)用的括號(hào)里, 也可以寫在外面, 例如:
func someFunctionThatTakesAClosure(closure: () -> Void) {
  // function body goes here
}
someFunctionThatTakesAClosure({})
someFunctionThatTakesAClosure(){}
// 又比如:
reversed = names.sort() { $0 > $1 }
// 還可以連()都省略
reversed = names.sort { $0 > $1 }

當(dāng)然, 這么寫肯定是要求閉包是最后一個(gè)參數(shù)的

  1. 值捕獲(Capturing Values)
    簡單說來就是閉包可以使用外部的一些變量或者常量, 即使外部已經(jīng)不存在了, 這里依然會(huì)保留, 直接看例子吧:
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen()
// 返回 10
incrementByTen()
// 返回 20
incrementByTen()
// 返回 30
// 由此可見, makeIncrementer函數(shù)內(nèi)部的runningTotal沒有被銷毀, 如果沒猜錯(cuò)的話, incrementByTen銷毀了才會(huì)銷毀掉(如果沒有別的引用了)
// 如果又創(chuàng)建了一個(gè)incrementor, 則會(huì)是新的一份runningTotal
let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// 返回 7

很顯然, 這里需要注意閉包中的循環(huán)引用問題

  1. 閉包是引用類型
    差不多記住這句話就好了, 所以要更加注意循環(huán)引用的問題

  2. 非逃逸閉包(Nonescaping Closures)
    官網(wǎng)解釋了很多, 但是聽起來還是云里霧里的, 最后總算搞明白了...簡單說來就是這個(gè)閉包被作為參數(shù)傳進(jìn)去之后, 如果函數(shù)返回了, 我們還能不能再用了, 默認(rèn)是可以再用, 如果你不想讓它被重復(fù)利用, 就加上@noescape, 通過例子來說吧:

var completionHandlers: [() -> Void] = [] // 存儲(chǔ)閉包用
func someFunctionWithNoescapeClosure(@noescape closure: () -> Void) {
    closure()
    completionHandlers.append(closure) // error
}
func someFunctionWithEscapingClosure(completionHandler: () -> Void) {
    completionHandlers.append(completionHandler)
}
someFunctionWithEscapingClosure { () -> Void in
            print("OK")
        }
completionHandlers.first?() // 把問號(hào)換成!也可以, 因?yàn)榇_定非空

加了@noescape的閉包, 必須當(dāng)場使用, 不允許出了函數(shù)再用, 但是沒有加的就可以.

  1. 自動(dòng)閉包(Autoclosures)
    還是回到Array的sort那里, 為什么可以省略掉{}呢? 因?yàn)橛凶詣?dòng)閉包, 所謂自動(dòng)閉包就是把傳入的參數(shù)自動(dòng)包裝成一個(gè)閉包, 直接看官方例子:
func serveCustomer(@autoclosure customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}
serveCustomer(customersInLine.removeAtIndex(0))
// 如果函數(shù)沒有@autoclosure則調(diào)用就是:
//serveCustomer({customersInLine.removeAtIndex(0)})

閉包差不多介紹到這里, 很多新的概念, 其實(shí)蘋果的宗旨就是, 能怎么簡潔就這么簡潔, 等習(xí)慣之后看起來還是很舒服, 很自然的.

按慣例, 具體細(xì)節(jié)看看官方文檔

后記

目前我也只看到這里, 所以一次更新到了閉包, 之后更新速度可能會(huì)慢, 下周還要出差...囧rz..感謝大家花時(shí)間看, 希望能有幫助, 也希望大家給出意見, 不對之處請指正.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末很魂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子攘滩,更是在濱河造成了極大的恐慌庭猩,老刑警劉巖窟她,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蔼水,居然都是意外死亡礁苗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門徙缴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嘁信,你說我怎么就攤上這事于样。” “怎么了潘靖?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵穿剖,是天一觀的道長。 經(jīng)常有香客問我卦溢,道長糊余,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任单寂,我火速辦了婚禮贬芥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宣决。我一直安慰自己蘸劈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布尊沸。 她就那樣靜靜地躺著威沫,像睡著了一般贤惯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上棒掠,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天孵构,我揣著相機(jī)與錄音,去河邊找鬼烟很。 笑死颈墅,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的溯职。 我是一名探鬼主播精盅,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼谜酒!你這毒婦竟也來了叹俏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤僻族,失蹤者是張志新(化名)和其女友劉穎粘驰,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體述么,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蝌数,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了度秘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顶伞。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖剑梳,靈堂內(nèi)的尸體忽然破棺而出唆貌,到底是詐尸還是另有隱情,我是刑警寧澤垢乙,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布锨咙,位于F島的核電站,受9級(jí)特大地震影響追逮,放射性物質(zhì)發(fā)生泄漏酪刀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一钮孵、第九天 我趴在偏房一處隱蔽的房頂上張望骂倘。 院中可真熱鬧,春花似錦油猫、人聲如沸稠茂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽睬关。三九已至诱担,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間电爹,已是汗流浹背蔫仙。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留丐箩,地道東北人摇邦。 一個(gè)月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像屎勘,于是被迫代替她去往敵國和親施籍。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

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