Swift 閉包
閉包(Closures)是自包含的功能代碼塊,可以在代碼中使用或者用來作為參數傳值。
Swift 中的閉包與 C 和 Objective-C 中的代碼塊(blocks)以及其他一些編程語言中的 匿名函數比較相似。
全局函數和嵌套函數其實就是特殊的閉包。
閉包的形式有:
全局函數 | 嵌套函數 | 閉包表達式 |
有名字但不能捕獲任何值。 | 有名字,也能捕獲封閉函數內的值。 | 無名閉包,使用輕量級語法,可以根據上下文環境捕獲值。 |
Swift中的閉包有很多優化的地方:
根據上下文推斷參數和返回值類型
從單行表達式閉包中隱式返回(也就是閉包體只有一行代碼,可以省略return)
可以使用簡化參數名,如$0, $1(從0開始,表示第i個參數...)
提供了尾隨閉包語法(Trailing closure syntax)
語法
以下定義了一個接收參數並返回指定類型的閉包語法:
{(parameters) -> return type in
實例
import Cocoalet studname = { print("Swift 閉包實例。") }studname()
以上程序執行輸出結果為:
Swift 閉包實例。
以下閉包形式接收兩個參數並返回布爾值:
{(Int, Int) -> Bool in
實例
import Cocoalet divide = {(val1: Int, val2: Int) -> Int in
以上程序執行輸出結果為:
10
閉包表達式
閉包表達式是一種利用簡潔語法構建內聯閉包的方式。 閉包表達式提供了一些語法優化,使得撰寫閉包變得簡單明了。
sorted 方法
Swift 標準庫提供了名為 sorted(by:) 的方法,會根據您提供的用於排序的閉包函數將已知類型數組中的值進行排序。
排序完成後,sorted(by:) 方法會返回一個與原數組大小相同,包含同類型元素且元素已正確排序的新數組。原數組不會被 sorted(by:) 方法修改。
sorted(by:)方法需要傳入兩個參數:
已知類型的數組
閉包函數,該閉包函數需要傳入與數組元素類型相同的兩個值,並返回一個布爾類型值來表明當排序結束後傳入的第一個參數排在第二個參數前面還是後面。如果第一個參數值出現在第二個參數值前面,排序閉包函數需要返回 true,反之返回false。
實例
import Cocoalet names = ["AT", "AE", "D", "S", "BE"]// 使用普通函數(或內嵌函數)提供排序功能,閉包函數類型需為(String, String) -> Bool。func backwards(s1: String, s2: String) -> Bool {
以上程序執行輸出結果為:
["S", "D", "BE", "AT", "AE"]
如果第一個字元串 (s1) 大於第二個字元串 (s2),backwards函數返回true,表示在新的數組中s1應該出現在s2前。 對於字元串中的字元來說,"大於" 表示 "按照字母順序較晚出現"。 這意味著字母"B"大於字母"A",字元串"S"大於字元串"D"。 其將進行字母逆序排序,"AT"將會排在"AE"之前。
參數名稱縮寫
Swift 自動為內聯函數提供了參數名稱縮寫功能,您可以直接通過$0,$1,$2來順序調用閉包的參數。
實例
import Cocoalet names = ["AT", "AE", "D", "S", "BE"]var reversed = names.sorted( by: { $0 > $1 } )print(reversed)
$0和$1表示閉包中第一個和第二個String類型的參數。
以上程序執行輸出結果為:
["S", "D", "BE", "AT", "AE"]
如果你在閉包表達式中使用參數名稱縮寫, 您可以在閉包參數列表中省略對其定義, 並且對應參數名稱縮寫的類型會通過函數類型進行推斷。in 關鍵字同樣也可以被省略.
運算符函數
實際上還有一種更簡短的方式來撰寫上面例子中的閉包表達式。
Swift 的String
類型定義了關於大於號 (>
) 的字元串實現,其作為一個函數接受兩個String
類型的參數並返回Bool
類型的值。 而這正好與sort(_:)
方法的第二個參數需要的函數類型相符合。 因此,您可以簡單地傳遞一個大於號,Swift可以自動推斷出您想使用大於號的字元串函數實現:
import Cocoalet names = ["AT", "AE", "D", "S", "BE"]var reversed = names.sorted(by: >)print(reversed)
以上程序執行輸出結果為:
["S", "D", "BE", "AT", "AE"]
尾隨閉包
尾隨閉包是一個書寫在函數括弧之後的閉包表達式,函數支持將其作為最後一個參數調用。
func someFunctionThatTakesAClosure(closure: () -> Void) {
實例
import Cocoalet names = ["AT", "AE", "D", "S", "BE"]//尾隨閉包var reversed = names.sorted() { $0 > $1 }print(reversed)
sort() 後的 { $0 > $1} 為尾隨閉包。
以上程序執行輸出結果為:
["S", "D", "BE", "AT", "AE"]
注意: 如果函數只需要閉包表達式一個參數,當您使用尾隨閉包時,您甚至可以把
()
省略掉。reversed = names.sorted { $0 > $1 }
捕獲值
閉包可以在其定義的上下文中捕獲常量或變數。
即使定義這些常量和變數的原域已經不存在,閉包仍然可以在閉包函數體內引用和修改這些值。
Swift最簡單的閉包形式是嵌套函數,也就是定義在其他函數的函數體內的函數。
嵌套函數可以捕獲其外部函數所有的參數以及定義的常量和變數。
看這個例子:
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
一個函數makeIncrementor ,它有一個Int型的參數amout, 並且它有一個外部參數名字forIncremet,意味著你調用的時候,必須使用這個外部名字。返回值是一個()-> Int
的函數。
函數題內,聲明了變數runningTotal 和一個函數incrementor。
incrementor函數並沒有獲取任何參數,但是在函數體內訪問了runningTotal和amount變數。這是因為其通過捕獲在包含它的函數體內已經存在的runningTotal和amount變數而實現。
由於沒有修改amount變數,incrementor實際上捕獲並存儲了該變數的一個副本,而該副本隨著incrementor一同被存儲。
所以我們調用這個函數時會累加:
import Cocoafunc makeIncrementor(forIncrement amount: Int) -> () -> Int {
以上程序執行輸出結果為:
102030
閉包是引用類型
上面的例子中,incrementByTen是常量,但是這些常量指向的閉包仍然可以增加其捕獲的變數值。
這是因為函數和閉包都是引用類型。
無論您將函數/閉包賦值給一個常量還是變數,您實際上都是將常量/變數的值設置為對應函數/閉包的引用。 上面的例子中,incrementByTen指向閉包的引用是一個常量,而並非閉包內容本身。
這也意味著如果您將閉包賦值給了兩個不同的常量/變數,兩個值都會指向同一個閉包:
import Cocoafunc makeIncrementor(forIncrement amount: Int) -> () -> Int {
以上程序執行輸出結果為:
50
※Swift 運算符
※Swift 條件語句
※Swift 循環
※Swift 字元串
※Swift 基本語法
TAG:程序員小新人學習 |
※iOS swift UISearchBar拿到textfield控制項
※Swift與Fedora
※Swift for TensorFlow 已在 GitHub 上開源,Tensor 成為 Swift 語言裡面的一等公民
※基於Swift 5的編程教學Swift Playgrounds即將推出
※介紹 Fedora 上的 Swift
※Swift 泛型
※Hermes愛馬仕 Bolide保齡球包 2H駝色 swift
※Hermes愛馬仕 Bolide保齡球包 3Q奶昔粉 swift
※Swift 的類
※谷歌宣布Swift for TensorFlow今日開源 附安裝包
※谷歌又開源了:Swift for TensorFlow
※Hermes愛馬仕 Bolide保齡球包 CK89黑色 swift
※可以拋棄 Python?Google 開源 Swift for TensorFlow 意味什麼
※蘋果 SwiftUI 踢館穀歌 Flutter
※宏碁Swift/Aspire與Spin PC系列產品全線更新
※Swift For TensorFlow開源,敲響了Python的喪鐘?
※Gigi Hadid和Taylor Swift超酷閨蜜裝
※可以拋棄 Python 了?Google 開源 Swift for TensorFlow 意味著什麼
※adidas Originals Swift Run PK推出全新配色
※開發者注意啦,谷歌宣布開源 Swift for TensorFlow