Swift4 閉包及其應(yīng)用 【Update】

閉包 closure

閉包在Swift中應(yīng)用廣泛,在許多系統(tǒng)庫方法中都能看到。

  • 無名
  • 本質(zhì)是函數(shù)
  • 使用時注意循環(huán)引用
閉包基礎(chǔ)
//閉包是無名的,因為他們能夠從上下文中捕獲變量、常量,從而應(yīng)用在自身的作用區(qū)域。

//聲明
var myClosure: () -> String
//注意 () -> String 實際上是一個無名函數(shù),作為閉包變量的類型。

//無返回值的閉包
myClosure:() -> Void = {
    print("Hello world")
}

//新版Xcode10中出現(xiàn)錯誤提示
//疑似以下兩個原因造成:
//1. 加強(qiáng)了 Swift 類型檢測, () -> String   ----> () -> Void  
//2. 閉包賦值時,無需在多余強(qiáng)調(diào)類型 
//故糾正如下:
myClosure = {
  print("Hello world")
  //這里根據(jù) 返回值類型予以糾正
 //() -> String
 //return "anyThing"
}

//執(zhí)行閉包
myClosure() // Hello world


//嘗試一個帶參的閉包
var mySecondClosure: (Int , Int ) -> Int = {
    (a: Int , b: Int) -> Int in 
    return a * b
}

//第一次簡化 類型自動推導(dǎo) 可以根據(jù)參數(shù)推斷
mySecondClosure = {
    (a, b) in 
    return a * b
}

//第二次簡化 如果函數(shù)體只包含一句 return 代碼,可省略 return
mySecondClosure = {
    (a, b) in 
    a * b
}

/*
第三次簡化 
被捕獲的參數(shù)列表中,含有a、b,下標(biāo)從0開始,可通過"$"獲取。

編譯器亦可通過,捕獲列表自行推斷出參數(shù)。
故可省略參數(shù)列表 (a, b)和 關(guān)鍵字 in 
*/
mySecondClosure = {
    $0 * $1
}

閉包作為函數(shù)參數(shù)
/*
operation: 是一個閉包類型的參數(shù)
該閉包有兩個參數(shù)(Int, Int) ,并有一個Int型返回值

要整體看待,不要蒙圈。
*/
func myOpreation(_ a: Int , _ b: Int, operation: (Int , Int) -> Int) -> Int {
    let res = operation(a, b)
    return res
}

//實現(xiàn)一個閉包
let multipyClosure:(Int, Int) -> Int = {
    $0 * $1
}

//等同于
let multipyClosure = {
    (a: Int, b: Int) in 
    a * b
}

//下面,我們將閉包作為參數(shù)傳遞
myOperation(4, 2, operation:multipyClosure)

//展開 inline
myOperation(4, 2, operation: {
    (a: Int, b: Int) -> Int in 
    return a * b
})


/*
事實上,我們并沒有必要在本地定義一個閉包常量或變量,再作為參數(shù)傳遞。
可以簡單的在調(diào)用的地方進(jìn)行聲明并簡化
*/

myOperation(4, 2, operation:{
    $0 * $1
})

//進(jìn)一步簡化,* 操作符是一個有兩個參數(shù)并返回一個結(jié)果的函數(shù)。可做如下簡化:
myOperation(4, 2, operation: *)

/*
如果閉包是作為函數(shù)的最后一個參數(shù),可以將閉包后置到函數(shù)體外部。 
*/
myOperation(4, 2) {
    $0 * $1
}

捕獲

閉包可以從上下文環(huán)境中捕獲常量、變量,并在自己的作用域內(nèi)使用。

//eg.: 1
var counter = 0
let incrementCounter = {
    counter += 1    
}

/*
由于閉包定義和變量counter在同一作用域中,
故閉包可以捕獲并訪問變量counter。
對變量counter做的任何改變,對閉包來說都是透明可見的。
*/

incrementCounter() //1
incrementCounter() //2

//eg.: 2
func countingClosure() -> () -> Int {
    var counter = 0
    let incrementCounter: () -> Int = {
    counter += 1
    return counter
    }
    return incrementCounter
}

/*
該例子中,閉包捕獲了封閉空間(函數(shù)實體內(nèi))的內(nèi)部變量counter。
*/


let counter1 = countingClosure()
let counter2 = countingClosure()

counter1() // 1
counter2() // 1
counter1() // 2
counter1() // 3
counter2() // 2

應(yīng)用閉包在集合中

排序
/*
數(shù)組提供了一個排序函數(shù),sorted().使用的是默認(rèn)規(guī)則,當(dāng)然我們也可以定制排序規(guī)則。
*/

let names = ["ZZZZZZ", "BB", "A", "CCCC", "EEEEE"]
names.sorted()
// ["A", "BB", "CCCC", "EEEEE", "ZZZZZZ"]

//更改排序規(guī)則 其實就是利用了函數(shù)重載,具體可看函數(shù)定義
//sorted(by: <#T##(String, String) throws -> Bool#>)

names.sorted {
    $0.count > $1.count
}
// ["ZZZZZZ", "EEEEE", "CCCC", "BB", "A"]

遍歷
集合提供了很多遍歷的函數(shù)用來對元素進(jìn)行訪問及操作,并大量應(yīng)用了閉包。
重點需要了解的函數(shù)有: 
forEach、filter、map、reduce
  • forEach
/*
循環(huán)遍歷集合中的元素,相當(dāng)于for-in 快速遍歷    
*/

let values = [5, 3, 2, 3,1]
values.forEach {
    print("element is \($0)")
}
  • filter
/*
函數(shù)原型:
 func filter(_ isIncluded: (Element) -> Bool) -> [Element]
 
 按照規(guī)則過濾原數(shù)組
*/

var values = [1.5, 10, 4.88, 3.2, 8]

let res = values.filter {
    return $0 > 4
}

//res是移除掉小于或等于4的元素的新數(shù)組
  • map
/*
函數(shù)原型:
students.map(<#T##transform: (String) throws -> T##(String) throws -> T#>)

數(shù)組映射
*/

let input = ["0", "12", "name", "hi", "3"]

let number = input.map {
    Int($0) //將元素轉(zhuǎn)換為Int型
}
//注意類型轉(zhuǎn)換可能失敗,所以返回的是可選型
//[Optional(0), Optional(12), nil, nil, Optional(3)]

/*
另外一個高度近似的函數(shù)
flatMap
隱含了兩種操作
1.解包
2.展開并合并
*/

let flatNumber = input.flatMap() {
    Int($0)
}
//[0, 12, 3]


  • reduce
/*
函數(shù)原型
reduce(<#T##initialResult: Result##Result#>, <#T##nextPartialResult: (Result, String) throws -> Result##(Result, String) throws -> Result#>)

*/

//數(shù)組
let flatNumber = [0, 12, 3]

let sum = flatNumber.reduce(0) {
    return $0 + $1
}
//15

//字典
let stock = [1.5: 5, 10: 2, 4.99: 20, 2.30: 5, 8.19: 30]
let stockSum = stock.reduce(0) {
  return $0 + $1.key * Double($1.value)
}
//384.5

/*
另一個reduce重載函數(shù)
注意inout 關(guān)鍵字,所以想想你可以怎么應(yīng)用?
*/
input.reduce(into: <#T##Result#>) { (<#inout Result#>, <#String#>) in
    <#code#>
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 閉包是自包含的函數(shù)代碼塊,可以在代碼中被傳遞和使用。Swift 中的閉包與 C 和 Objective-C 中的代...
    窮人家的孩紙閱讀 1,811評論 1 5
  • 以下翻譯自Apple官方文檔,結(jié)合自己的理解記錄下來。翻譯基于 swift 3.0.1 原文地址 Closure...
    藝術(shù)農(nóng)閱讀 1,713評論 0 3
  • 閉包 閉包是自包含的功能塊,可以在代碼中傳遞和使用。Swift中的閉包與C和Objective-C中的塊以及其他編...
    Fuuqiu閱讀 447評論 0 0
  • C++ 繼承 派生類在繼承時默認(rèn)的訪問修飾符為 private,一般都會使用 public,這樣才能繼承基類 pu...
    謝小帥閱讀 377評論 0 0
  • 132566889
    Sunshine_d1b0閱讀 262評論 0 0

友情鏈接更多精彩內(nèi)容