標籤:swift
swift的閉包和c,oc中block是差不多的,其實swift中的全域和嵌套函數實際上也是一種特殊的閉包,閉包有三種形式:
(1)全域函數是有一個名字,但不能捕獲任何值的閉包
(2)嵌套函數是有一個名字,可以捕獲在函數內的值得閉包
(3)閉包運算式是一個輕量級的文法,可以從上下文中捕獲值
閉包表達是一個乾淨,清晰,鼓勵簡介,最佳化的文法,最佳化包括:
(1)從上下文中推斷參數和傳回型別
(2)簡單的閉包表達可以省略return
(3)簡寫參數名
(4)尾部閉包文法
(一)閉包表達
閉包表達就是以一個簡明準確的方式寫一個內聯包
(二)sort函數
swift標準庫提供了sort函數,用來排序已知類型的數組,通過你提供的排序包規則輸出
let originalNames = ["Chris","Alex","Ewa","Barry","Daniella"] var currentNames = originalNames.sort(backwards)
func backwards(s1: String , s2: String) -> Bool { return s1 > s2 }
這麼表達有點囧長啊(long-winded),但是在這這個栗子也算比較好和合適的
下面用閉包表達一下
var reverse = originalNames.sort({ (s1: String , s2: String) -> Bool in return s1 > s2 })
解釋:(1)in前面的是參數類型和返回值類型
(2)in後面是實現部分
(3)整個運算式用{}號括起來
由於swift有推斷類型功能,閉包可以從上下文推斷類型,所以上面的運算式還可以寫成:
var reverse = originalNames.sort({s1 , s2 in return s1 > s2}) print(reverse)
另外,單行運算式可以忽略return關鍵字,所以上面的運算式又可以寫成:
var reverse = originalNames.sort({s1 , s2 in s1 > s2}) print(reverse)
通過sort函數和sort的第二個參數我們就可知道這個函數一定是返回一個bool類型,返回值只有一個單一的運算式,所以return可以省略,並且不會造成歧義
(三)簡寫參數名
swift對於內聯包自動提供了參數名的簡寫,這些簡寫名用來關聯參數,那麼這些看不到簡寫名是怎麼簡寫的呢?就是$0,$1,$2....
如果你寫閉包表達用了簡寫名,那你寫的所有參數就可以省了,因為人家已經相當於給你提供了參數,只是簡寫了,就不用你寫了,寫了都TM浪費感情啊,不光浪費自己的,還浪費swift的,尤其是人家程式員的,我都把閉包簡成這樣了,你就不要寫那麼麻煩添亂了,省的大家都心裡堵得慌,廢話不多說了,下面檢測一下這個簡寫:
var reverse = originalNames.sort( { $0 > $1 } ) print(reverse)
0,1這個就是從第一個參數開始關聯,大家都是幹程式的,知道程式排序都是從0開始吧,這個你在問為什麼,我就只能呵呵了
(四)操作符(有的也叫運算子)函數
由於swift的String類型有規定兩個字元的比較,並且返回的是Bool類型,所以上面我們可以寫的再簡單點,直接傳一個操作符(運算子),swift就會自己推斷調用string自身規定的實現
var reverse = originalNames.sort(>)
(五)Trailing(尾部)閉包
意思就是把閉包實現放在尾部來寫,為什麼要放在尾部呢,因為閉包實現太長了,可能會影響代碼的可讀性,美觀性,所以就放在最後面實現好了
swift數組有一個map(映射)方法,僅僅是採用閉包表達作為參數像裡面傳遞的。這個閉包會遍曆數組的每個元素,並且返回相應的可選類型的映射。映射的核心和返回值的類型都是又閉包規定的。當使用map方法後,會返回一個新的數組, 這個數組包含了所有新的映射的值,下面舉一個例子(把Int型數群組轉換成Strin型的數組)說明一下:
let digitNames = [0:"Zero",1:"One",2:"Two",3:"Three",4:"Four",5:"Five",6:"Six",7:"Seven",8:"Eight",9:"Nine"] let numbers = [16,58,510] let strings = numbers.map() { (var number) -> String in var output = "" while number > 0 { output = digitNames[number % 10]! + output number /= 10 } return output } print(strings)
解釋:首先聲明一個字典,在聲明一個數組,調用map方法,自己寫實現
其次,通過return,swift已經推斷strings是一個字串型的數組了
最後,就看列印結果了
如果閉包是函數的唯一參數,函數後面的括弧可以省略,所以上面的函數還可以這麼寫:
let strings2 = numbers.map { (var number) -> String in var output = "" while number > 0 { output = digitNames[number % 10]! + output number /= 10 } return output } print(strings2)
在這裡,number前面使用var修飾的,這個在函數有講,就是在內部調用可變,相當於在函數內部聲明了一個變數,並把number(數組的元素)傳遞給了這個變數,函數調用結束,他也就釋放了
規定的傳回型別是String類型,儲存在返回的數組中了
注意:上面字典後面跟了一個驚嘆號,因為字典下標文法總是返回一個可選值的如果查詢失敗的話,但是在上面的例子中,一定要確保字典下標值是有效地,因此我們要強制拆包,得到儲存在可選返回值的中的String類型值
(六)值捕獲
說白了,就是閉包可以從上下文中捕獲值,用為己用(變數常量都可以,還可以自己改變其值),下面舉一個簡單的函數嵌套的例子
let incrementByTen = makeIncrementer(forIncrement: 10) print(incrementByTen()) } func makeIncrementer(forIncrement amout: Int) -> Void -> Int { var runningTotal = 0 func increment() -> Int { runningTotal += amout return runningTotal } return increment }
func increment()這個函數就捕獲到了runningTotal和amout的值,makeIncrementer這個函數返回的是一個函數類型(一個不接收參數且返回值類型為Int類型的函數)
let incrementByTen = makeIncrementer(forIncrement: 10) incrementByTen() incrementByTen() print(incrementByTen())
上面的列印結果猜一下是多少?這是因為閉包自動幫我們儲存了變數的值,調動同一函數會自動加,如果我們再定義一個函數呢?比如下面兩個的輸出結果
let incrementBySeven = makeIncrementer(forIncrement: 7) incrementByTen() print(incrementByTen()) print(incrementBySeven())
由上面得知我們再定義一個函數閉包會引用一個新值給這個新的函數,和前面的函數不會混淆,而原始的還是執行自己的功能
注意:如果我們把一個閉包定義成了類執行個體的屬性,通過引用這個執行個體或者它的成員,會產生一個強引用,不過給了我們解決辦法,這個我會在後面學習之後進行講解,謝謝
(七)閉包是參考型別
在上面的例子中,incrementByTen和incrementBySeven是常量,但是他們引用的閉包仍然可以改變runningTotal的值,這是因為函數和閉包都是參考型別的,當我們把函數或者閉包賦給一個常量或者變數時,實際上是我們用這個常量或者變數引用那個閉包,列印裡面調用了也算也用,在列印下面的代碼中runningTotal的值已經被+10了,
let alsoIncrementByTen = incrementByTen
這裡面的alsoIncrementByTen是incrementByTen+10的結果,不信可以自己嘗試下
好啦,閉包也講完了,下次我會介紹swift中的枚舉
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
swift學習之閉包(closure)