14.Swift學(xué)習(xí)之閉包

閉包引入

計算1個數(shù)的平方

  • 函數(shù)寫法
func square(param:Int)  -> Int{  
    return param * param
}
square(param:3)
  • 閉包寫法
let squareCloure = { (param:Int) -> Int in
    return param * param
}
squareCloure(3)

閉包含義

  • 閉包是可以被傳遞和引用的一個獨立模塊
  • 閉包能夠捕獲和存儲定義在其上下文中的任何常量和變量,即閉合并包裹那些常量和變量,因此被稱為“閉包”
  • 閉包符合如下三種形式中的一種:
    • 全局函數(shù)是一個有名字但不會捕獲任何值的閉包
    • 內(nèi)嵌函數(shù)是一個有名字且能從其上層函數(shù)捕獲值的閉包(函數(shù)中的嵌套函數(shù)知識點)
    • 閉包表達(dá)式是一個輕量級語法,可以捕獲其上下文中常量或變量值的沒有名字的閉包
  • 閉包和函數(shù)一樣也是引用類型

簡單案例

  • 案例一
let demo= { print("Swift 閉包實例。") }
demo()
  • 案例二
let divide = {(val1: Int, val2: Int) -> Int in 
   return val1 / val2 
}
let result = divide(200, 20)
print (result)

閉包表達(dá)式

閉包表達(dá)式語法有如下的一般形式:

  { (parameters) -> (return type) in
    statements
  }
  • 閉包表達(dá)式由一對{}開始與結(jié)束
  • in關(guān)鍵字將閉包分割成兩部分:參數(shù)與返回值、閉包體
  • 閉包參數(shù)與函數(shù)參數(shù)的區(qū)別
    • 形式參數(shù)不能提供默認(rèn)值,其他和函數(shù)一樣

閉包主要知識點

參數(shù)名稱縮寫
  • Swift 提供了參數(shù)名稱的縮寫功能,直接通過 $0,$1,$2來順序調(diào)用閉包的參數(shù)
  • 在閉包表達(dá)式中使用參數(shù)名稱縮寫,可以在閉包參數(shù)列表中省略對其定義
    • 參數(shù)類型可以通過函數(shù)類型進(jìn)行推斷
    • 在單行閉包的時候,return 關(guān)鍵字可以省略
    • 參數(shù)名稱省略以后,in 關(guān)鍵字也可以被省略
//從數(shù)組中篩選指出合適的數(shù)據(jù)組成新的數(shù)組
func getList(score:[Int], con:(Int)->Bool) -> [Int]{
    
    var newScore:[Int] = [Int]()
    for item in score {
        if con(item) {
            newScore.append(item)
        }
    }
    return newScore
}

let newAarray = getList(score: [75,60,95,45,85],  con:{(s:Int)->Bool in return s>80})
print(newAarray)

第一種簡寫: 省略 ->與返回類型(根據(jù)后面表達(dá)式可以推斷返回值是一個Bool)

let newAarray = getList(score: [75,60,95,45,85],  con:{(s:Int) in return s>80})

第二種簡寫:省略參數(shù)類型和括號(根據(jù)函數(shù)的參數(shù)可推斷傳進(jìn)來的必然是Int)

let newAarray = getList(score: [75,60,95,45,85],  con:{s in return s>80})

第三種簡寫:省略return關(guān)鍵字

let newAarray = getList(score: [75,60,95,45,85],  con:{s in s>80})

第四種簡寫:參數(shù)名稱縮寫,省略參數(shù)聲明和in,改為$0

let newAarray = getList(score: [75,60,95,45,85],  con:{$0>80})
捕獲
  • 閉包可以從上下文環(huán)境中捕獲常量、變量,并在自己的作用域內(nèi)使用
  • Swift最簡單的閉包形式是嵌套函數(shù),也就是定義在其他函數(shù)的函數(shù)體內(nèi)的函數(shù),嵌套函數(shù)可以捕獲其外部函數(shù)所有的參數(shù)以及定義的常量和變量。
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}

let incrementByTen = makeIncrementor(forIncrement: 10)
// 返回的值為10
print(incrementByTen())
// 返回的值為20
print(incrementByTen())
// 返回的值為30
print(incrementByTen())
尾隨閉包
  • 尾隨閉包是一個書寫在函數(shù)括號之后的閉包表達(dá)式,函數(shù)支持將其作為最后一個參數(shù)調(diào)用
    • 閉包是函數(shù)的最后一個參數(shù)
    • 調(diào)用時,函數(shù)的 ) 可以前置到倒數(shù)第二個參數(shù)末尾,后面的參數(shù)直接使用 { // 執(zhí)行代碼 },形式參數(shù)標(biāo)簽也隨之省略
    • 將一個很長的閉包表達(dá)式作為最后一個參數(shù)傳遞給函數(shù),可以使用尾隨閉包來增強(qiáng)函數(shù)的可讀性
//尾隨閉包
func doSomething(info:String, clousre:(String)->Void){  
     clousre(info)
}

//不使用尾隨閉包進(jìn)行函數(shù)調(diào)用
doSomething(info: "Hello", clousre: { s in print(s) })

//使用尾隨閉包進(jìn)行函數(shù)調(diào)用
doSomething(info: "World") { s in
    print(s)
}
逃逸閉包
  • 閉包作為一個參數(shù)傳遞給一個函數(shù)
  • 傳入函數(shù)的閉包如果在函數(shù)執(zhí)行結(jié)束之后才會被調(diào)用,那么這個閉包就叫做逃逸閉包
  • 聲明一個接受閉包作為形式參數(shù)的函數(shù)時,可以在形式參數(shù)的類型之前寫上@escaping 來明確閉包是允許逃逸的
  • 逃逸閉包會在函數(shù)結(jié)束后才執(zhí)行
  • 舉例
//逃逸閉包:閉包可以超出函數(shù)的范圍來調(diào)用
//存放沒有參數(shù)、沒有返回值的閉包
var closureArray :[()->Void] = [()->Void]()
//定義一個函數(shù),接收一個非逃逸閉包為參數(shù)
func nonEscapeClosure(closure:()->Void){  
    closure()
}
//定義一個函數(shù),接收一個逃逸閉包為參數(shù),將閉包并存儲到一個數(shù)組里面去,并沒有調(diào)用
func escapeClosure(closure: @escaping ()->Void){
    print("函數(shù)開始")
    closureArray.append(closure)
    print("函數(shù)結(jié)束")
}
var x = 10
//打印10
print(x)

nonEscapeClosure {
    x = 100
}
//打印100 因為閉包在函數(shù)里面執(zhí)行了
print(x)

escapeClosure {
    x = 200
}
//打印100 因為閉包逃逸了 沒有在函數(shù)里面執(zhí)行
print(x)

closureArray.first?()
//打印200 在函數(shù)外面調(diào)用了閉包
print(x)

//尾隨閉包常用于異步回調(diào)
自動閉包
  • 一種自動創(chuàng)建的閉包,用于包裝函數(shù)參數(shù)的表達(dá)式
  • 不接受任何參數(shù),被調(diào)用時會返回被包裝在其中的表達(dá)式的值
  • 在形式參數(shù)的類型之前加上@autoclosure關(guān)鍵字標(biāo)識這是一個逃逸閉包
//自動閉包
func printIfTrue(predicate:@autoclosure ()->Bool){  
    if predicate() {
        print("is true")   
    }    
    else{       
        print("is false")       
    }   
}

//直接進(jìn)行調(diào)用了,Swift 將會把 2 > 1 這個表達(dá)式自動轉(zhuǎn)換為 () -> Bool。這樣我們就得到了一個寫法簡單、表意清楚的表達(dá)式。
printIfTrue(predicate: 2>1)
printIfTrue(predicate: 2<1)

閉包的循環(huán)引用

class NetworkTools: NSObject {
/// 完成回調(diào)屬性
var finishedCallBack: (()->())?
/// 加載數(shù)據(jù)
/// - parameter finished: 完成回調(diào)
func loadData(finished: () -> ()) {

    self.finishedCallBack = finished
    working()
}

func working() {
    finishedCallBack?()
}

deinit {
    print("網(wǎng)絡(luò)工具 88")
}

class ViewController: UIViewController {

    var tools: NetworkTools?
    override func viewDidLoad() {
        super.viewDidLoad()

        tools = NetworkTools()
        tools?.loadData() {
           print("加載數(shù)據(jù)完成,更新界面:", NSThread.currentThread())
           weakSelf!.view.backgroundColor = UIColor.redColor()
        }
    }
    /// 與 OC 中的 dealloc 類似,注意此函數(shù)沒有()
    deinit {
        print("控制器 88")
    }
}

Swift中解決循環(huán)引用的方式
  • 方案一:
    • 使用weak,對當(dāng)前控制器使用弱引用
    • 但是因為self可能有值也可能沒有值,因此weakSelf是一個可選類型,在真正使用時可以對其強(qiáng)制解包(該處強(qiáng)制解包沒有問題,因為控制器一定存在,否則無法調(diào)用所在函數(shù))
// 解決方案一:
weak var weakSelf = self
tools.loadData {
    print("加載數(shù)據(jù)完成,更新界面:", NSThread.currentThread())
    weakSelf!.view.backgroundColor = UIColor.redColor()
}
  • 方案二:
  • 和方案一類型,只是書寫方式更加簡單
  • 可以寫在閉包中,并且在閉包中用到的self都是弱引用
tools.loadData {[weak self] () -> () in
    print("加載數(shù)據(jù)完成,更新界面:", NSThread.currentThread())
    self!.view.backgroundColor = UIColor.redColor()
}
  • 方案三:
    • 使用關(guān)鍵字unowned
    • 從行為上來說 unowned 更像OC中的 unsafe_unretained
    • unowned 表示:即使它原來引用的對象被釋放了,仍然會保持對被已經(jīng)釋放了的對象的一個 "無效的" 引用,它不能是 Optional 值,也不會被指向 nil
tools.loadData {[unowned self] () -> () in
    print("加載數(shù)據(jù)完成,更新界面:", NSThread.currentThread())
    self.view.backgroundColor = UIColor.redColor()
}
最后編輯于
?著作權(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)容

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