Swift基礎語法-閉包,閉包函數(shù)回調(diào),尾隨閉包,閉包捕獲值,循環(huán)引用


本節(jié)知識點

  1. 閉包的基本概念
  2. 閉包基本使用
  3. 閉包表達式作為回調(diào)函數(shù)
  4. 閉包的多種寫法(尾隨閉包)
  5. 閉包表達式優(yōu)化
  6. 閉包捕獲值
  7. <a href="#閉包的循環(huán)引用(重點)">閉包的循環(huán)引用(重點)</a>

1. 閉包的基本概念

  • 閉包是一種類似于OC語言的block 匿名函數(shù)
  • block閉包都經(jīng)常用于回調(diào)
  • 閉包表達式(匿名函數(shù)) 能夠捕獲上下文中的值
  • 閉包格式:
{
    (參數(shù)) -> 返回值類型 in
    執(zhí)行語句
}
  • 閉包表達式的類型和函數(shù)的類型一樣, 是參數(shù)加上返回值, 也就是in之前的部分
  • 語法: in關(guān)鍵字的目的是便于區(qū)分返回值和執(zhí)行語句

2. 閉包基本使用

  • ** 有參數(shù)沒有返回值寫法**
let say0:(String) ->Void = {
    (name: String) in
    print("hi \(name)")
}
say0("cdh")
//輸出結(jié)果:  hi cdh
  • 沒有參數(shù)沒有返回值寫法
let say1:() ->Void = {
    () -> () in // 當沒有參數(shù)也沒有返回值是這行都可以省略
    print("hi cdh")
}
say1()
//輸出結(jié)果:  hi cdh
  • 有參數(shù)有返回值
略; 按照格式, 結(jié)合函數(shù)的寫法即可
  • 沒有參數(shù)有返回值

3. 閉包表達式作為回調(diào)函數(shù)

3.1 block的用法回顧
  • 定義網(wǎng)絡請求的類
@interface HttpTool : NSObject
- (void)loadRequest:(void (^)())callBackBlock;
@end

@implementation HttpTool
- (void)loadRequest:(void (^)())callBackBlock
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"加載網(wǎng)絡數(shù)據(jù):%@", [NSThread currentThread]);

        dispatch_async(dispatch_get_main_queue(), ^{
            callBackBlock();
        });
    });
}
@end
  • 進行網(wǎng)絡請求,請求到數(shù)據(jù)后利用block進行回調(diào)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self.httpTool loadRequest:^{
        NSLog(@"主線程中,將數(shù)據(jù)回調(diào).%@", [NSThread currentThread]);
    }];
}
  • block寫法總結(jié):
//block的寫法:
//類型定義:
返回值(^block的名稱)(block的參數(shù))

//等號右邊的值:
^(參數(shù)列表) {
    // 執(zhí)行的代碼
};
3.2 使用閉包代替block
  • 定義網(wǎng)絡請求的類
class HttpTool: NSObject {

    func loadRequest(callBack : ()->()){
        dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
            print("加載數(shù)據(jù)", [NSThread.currentThread()])

             dispatch_async(dispatch_get_main_queue(), { () -> Void in
                callBack()
             })
        }
    }
}
  • 進行網(wǎng)絡請求,請求到數(shù)據(jù)后利用閉包進行回調(diào)
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        // 網(wǎng)絡請求
        httpTool.loadRequest ({ () -> () in
            print("回到主線程", NSThread.currentThread());
        })
    }
  • 閉包寫法總結(jié):
//閉包的寫法:
// 類型:(形參列表)->(返回值)
// 技巧:初學者定義閉包類型,直接寫()->().再填充參數(shù)和返回值

//值:
{
    (形參) -> 返回值類型 in
    // 執(zhí)行代碼
}
3.3 閉包的簡寫
  • 如果閉包沒有參數(shù),沒有返回值.in和in之前的內(nèi)容可以省略
    httpTool.loadRequest({
        print("回到主線程", NSThread.currentThread());
    })

4. 閉包的多種寫法(尾隨閉包)

  • 如果閉包是函數(shù)的最后一個參數(shù),則可以將閉包寫在()后面
  • 如果函數(shù)只有一個參數(shù),并且這個參數(shù)是閉包,那么()可以不寫
  • 這樣可以提高閱讀性. 稱之為尾隨閉包
    httpTool.loadRequest() {
        print("回到主線程", NSThread.currentThread());
    }
    // 開發(fā)中建議該寫法
    httpTool.loadRequest {
        print("回到主線程", NSThread.currentThread());
    }

5. 閉包表達式優(yōu)化

  • 類型優(yōu)化, 由于函數(shù)中已經(jīng)聲明了閉包參數(shù)的類型, 所以傳入的實參可以不用寫類型
  • 返回值優(yōu)化, 同理由于函數(shù)中已經(jīng)聲明了閉包的返回值類型, 所以傳入的實參可以不用寫類型
  • 參數(shù)優(yōu)化, swift可以使用$索引的方式來訪問閉包的參數(shù), 默認從0開始
// 比較大小排序
func bubbleSort(inout array:[Int], cmp: (Int, Int) -> Int) {
    let count = array.count;
    // i = 1; i < count; i++ 推薦寫成 i in 1..< count 
    // 在Swift3.0 也可能就只有后者這種寫法
    for var i = 1; i < count; i++ {
        for var j = 0; j < (count - i); j++ {
            if cmp(array[j], array[j + 1]) == -1 {
                let temp = array[j]
                array[j] = array[j + 1]
                array[j + 1] = temp
            }
        }
    }
}
var arr:Array<Int> = [31, 13, 52, 84, 5]
print("排序前 \(arr)")
//輸出結(jié)果: 排序前 [31, 13, 52, 84, 5]
bubbleSort(&arr){
//    (a , b) -> Int in
//    (a , b) in
    if $0 > $1{
        return 1;
    }else if $0 < $1 {
        return -1;
    }else {
        return 0;
    }
}
print("排序后 \(arr)")
//輸出結(jié)果: 排序后 [84, 52, 31, 13, 5]
  • 如果只有一條語句可以省略return
let hehe = {
    "我是cdh"
}

6. 閉包捕獲值

  • 被捕獲的值會和與之對應的方法綁定在一起, 下一次需要用到這個方法這繼續(xù)使用之前捕獲到的值
  • 同一個方法中的變量, 不會被綁定到不同的方法變量(或常量中)
func getIncFunc() -> (Int) -> Int {
    var max = 10
    func incFunc(x :Int) ->Int{
        print("incFunc函數(shù)結(jié)束")
        max++  // ++ 這種寫法將在 swift3.0 移除, 推薦寫成 += 1
        return max + x
    }
    //當執(zhí)行到這一句時incFunc 的參數(shù) x 就應該被釋放了
    //但是由于在內(nèi)部函數(shù)中使用到了它, 所以它被捕獲了
    //同理, 當執(zhí)行完這一句時max變量就被釋放了
    //但是由于在內(nèi)部函數(shù)中使用到了它, 所以它被捕獲了
    print("getIncFunc函數(shù)結(jié)束")
    return incFunc
}
let incFunc = getIncFunc()
print("---------")
print(incFunc(5))
print("---------")
print(incFunc(5))
//輸出結(jié)果: 
//getIncFunc函數(shù)結(jié)束
//---------
//incFunc函數(shù)結(jié)束
//16
//---------
//incFunc函數(shù)結(jié)束
//17

let incFunc2 = getIncFunc() 
print(incFunc2(5))
//輸出結(jié)果:
//getIncFunc函數(shù)結(jié)束
//incFunc函數(shù)結(jié)束
//16

7. <a name = "閉包的循環(huán)引用(重點)"></a>閉包的循環(huán)引用(重點)

  • 閉包形成的循環(huán)引用與 block 的循環(huán)引用是一樣的原因?qū)е? 如下閉包形成的循環(huán)引用的示例:
強引用 loadData函數(shù)的參數(shù)(閉包). png
loadData函數(shù)中參數(shù)(閉包)強引用 self(ViewController).png

//  ViewController.swift
//  03-13-閉包的循環(huán)引用
//
//  Created by chendehao on 16/3/2.
//  Copyright ? 2016年 CDH. All rights reserved.
//

import UIKit

class ViewController: UIViewController {

    var httpTool : HttpTools?
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        httpTool = HttpTools()
    }
    
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        
        httpTool?.loadData(){ (jsonData) in
            print("在 viewController中拿到數(shù)據(jù): \(jsonData)")
            self.view.backgroundColor = UIColor.grayColor()
            // self 強引用這類屬性 httpTool, httpTool 中的函數(shù) loadData 中的閉包參數(shù)在這里引用者 self (ViewController) 
            // 如果此次該閉包參數(shù)在 HttpTool 類中沒有被強引用, 則此時將不形成循環(huán)引用,因為閉包是臨時存儲, 用完就被釋放
            // 也就只有self (ViewController) 強引用這類屬性 httpTool, 而 HttpTool 并沒有強引用 self
            
            // 但是如果在 HttpTool 類中強引用了 loadData函數(shù)中的閉包這個參數(shù), 則此時就會形成循環(huán)引用
            
        }
    }

    // 該對象被釋放都會調(diào)用這個函數(shù)
    deinit{
        print("ViewController -- deinit")
    }
}
//  HttpTools.swift
//  03-13-閉包的簡單使用
//
//  Created by chendehao on 16/3/2.
//  Copyright ? 2016年 CDH. All rights reserved.
//

import UIKit

class HttpTools: NSObject {
    
    var finishedCallBack :((jsonData : String) -> ())?
    
    
    // 閉包的類型寫法: (參數(shù)列表) -> (返回值類型)
    func loadData(finishedCallback : (jsonData : String) -> ())  {
        // 此處對函數(shù)中的閉包參數(shù)通過屬性強引用
        self.finishedCallBack = finishedCallback
        
        dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
            print("正在發(fā)送網(wǎng)絡請求: \(NSThread.currentThread())")
            
            dispatch_sync(dispatch_get_main_queue(), { 
                () -> Void in
                print("回調(diào)主線程, 見數(shù)據(jù)回傳出去, \(NSThread.currentThread())")
                
                finishedCallback(jsonData: "json數(shù)據(jù)")
            })
        }
    }
}
  • 通過以上兩段代碼已經(jīng)形成了強引用, 這樣一來就會形成內(nèi)存泄漏問題
7.1 解決循環(huán)引用
  • 未免出現(xiàn)以上的循環(huán)引用導致內(nèi)存泄漏的問題, 有多種解決辦法
  • 方法一: 刪除對 loadData 函數(shù)中的閉包參數(shù)做強引用即可
// 刪除這個即可
        // 此處對函數(shù)中的閉包參數(shù)通過屬性強引用
        self.finishedCallBack = finishedCallback
  • 方法二(重點掌握): 將 self (ViewController) 使用弱引用關(guān)鍵之修飾
// 寫法一: 定義一個 weak 修飾的新的臨時變量
weak var weakSelf: ViewController? = self
httpTool?.loadData({ (jsonData) ->() in

    weakSelf?.view.backgroundColor = UIColor.orangeColor()
    print("在 viewController中拿到數(shù)據(jù): \(jsonData)")
})
// 寫法二: 直接使用 [weak self] 寫在閉包的大括號中

// 尾隨閉包 : 如果在調(diào)用方法時,該方法的最后一個參數(shù)是一個閉包.那么該閉包可以寫成尾隨閉包
// 尾隨閉包寫法一: (推薦將 [weak self]寫在閉包的大括號中的寫法)
httpTool?.loadData(){ [weak self] (jsonData) in
    print("在 viewController中拿到數(shù)據(jù): \(jsonData)")
    self?.view.backgroundColor = UIColor.grayColor()
}

// 尾隨閉包寫法二:
httpTool?.loadData { [unowned self] (jsonData) in
    print("在 viewController中拿到數(shù)據(jù): \(jsonData)")
    self.view.backgroundColor = UIColor.lightGrayColor()
}

// 注意 weak 和 unowned 的區(qū)別, 查看<<內(nèi)存管理>>一文有詳細說明

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市伤疙,隨后出現(xiàn)的幾起案子勋锤,更是在濱河造成了極大的恐慌倒慧,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宅荤,死亡現(xiàn)場離奇詭異智哀,居然都是意外死亡,警方通過查閱死者的電腦和手機彰檬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谎砾,“玉大人逢倍,你說我怎么就攤上這事」桌疲” “怎么了瓶堕?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵隘道,是天一觀的道長症歇。 經(jīng)常有香客問我,道長谭梗,這世上最難降的妖魔是什么忘晤? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮激捏,結(jié)果婚禮上设塔,老公的妹妹穿的比我還像新娘。我一直安慰自己远舅,他們只是感情好闰蛔,可當我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著图柏,像睡著了一般序六。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚤吹,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天例诀,我揣著相機與錄音随抠,去河邊找鬼。 笑死繁涂,一個胖子當著我的面吹牛拱她,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播扔罪,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼秉沼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了步势?” 一聲冷哼從身側(cè)響起氧猬,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎坏瘩,沒想到半個月后盅抚,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡倔矾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年妄均,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哪自。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡丰包,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出壤巷,到底是詐尸還是另有隱情邑彪,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布胧华,位于F島的核電站寄症,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏矩动。R本人自食惡果不足惜有巧,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悲没。 院中可真熱鬧篮迎,春花似錦、人聲如沸示姿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽栈戳。三九已至岂傲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荧琼,已是汗流浹背譬胎。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工差牛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人堰乔。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓偏化,卻偏偏與公主長得像,于是被迫代替她去往敵國和親镐侯。 傳聞我的和親對象是個殘疾皇子侦讨,可洞房花燭夜當晚...
    茶點故事閱讀 45,107評論 2 356

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