Swift閉包實戰(zhàn)

尊重知識桨嫁,轉(zhuǎn)發(fā)請注明出處:Swift閉包實戰(zhàn)


概要

接觸過Swift的小伙伴對“閉包”應該不陌生,相當于OC中的Block份帐,是Swift語法中比較難理解的一塊璃吧。

swift

無論蘋果的官方文檔還是由官方文檔衍生出來的一些文章和書籍都比較重視基礎語法知識的講解,對于實戰(zhàn)中的應用提及的都很少废境,所以當我們想使用“閉包”解決一些問題的時候畜挨,會忽然出現(xiàn)看著一堆理論知識卻不知從何下手的尷尬感筒繁,這就是理論和時實戰(zhàn)的區(qū)別了。

本文不贅述Swift閉包的的基本語法了巴元,百度或者Google下有很多資料毡咏,我平時會閱讀極客學院的官方翻譯文檔:閉包。如題所示本文著重講述Swift閉包的一些實戰(zhàn)案例逮刨,有需要的小伙伴可以參考下呕缭,經(jīng)驗豐富的大神也請指教。

關于如何理解閉包

學習閉包的第一個難點就是理解閉包修己,可能很多人用了很久的閉包都還不太清楚閉包到底是什么恢总,我這里提供一種理解思路,僅供參考睬愤。

對于很多iOS開發(fā)者來說一開始接觸到Swift閉包會試圖用OC中的Block去理解片仿,當然這會對我們的理解有一定幫助,就好比很多人學習英語:tomato->西紅柿->??尤辱,而不是tomato-> ??砂豌,而一個嬰兒剛開始接觸語言時候就是直接由tomato的發(fā)音聯(lián)想到??,而后再去學習??的單詞拼寫光督,這是人類與生俱來的學習語言的邏輯流程阳距,所以我們不妨按照這種思維邏輯去學習和理解Swift的閉包。

1可帽、閉包是什么
這就好比一個嬰兒好奇西紅柿是什么,家長會將一個真實的西紅柿拿到他的面前窗怒,看一看映跟,摸一摸,聞一聞扬虚,嘗一嘗努隙。對于Swift的閉包,我們首先不需要知道是它在語法上如何定義的辜昵,而是要知道閉包的本質(zhì)荸镊。

閉包的本質(zhì)是代碼塊,它是函數(shù)的升級版本堪置,函數(shù)是有名稱躬存、可復用的代碼塊,閉包則是比函數(shù)更加靈活的匿名代碼塊舀锨。

2岭洲、為什么需要閉包
當一個嬰兒知道了西紅柿是什么,自然而然就會想到西紅柿有什么用坎匿,那么我們自然也會問閉包在Swift中有何用處呢盾剩?

函數(shù)已經(jīng)可以滿足我們開發(fā)中大部分的需求了雷激,那么為什么還需要閉包呢。在開發(fā)中我們經(jīng)常需要傳遞各種數(shù)據(jù)告私,我們習慣了傳遞一個值:Int屎暇,一串符號:String,一個對象:Class驻粟,但是有時我們需要傳遞一種處理問題的邏輯根悼,我們常用的類型似乎滿足不了這種需求,而函數(shù)恰好是一種處理問題的邏輯格嗅,為了讓函數(shù)像Int番挺、Float、String等常用類型一樣靈活的傳遞和調(diào)用屯掖,閉包就出現(xiàn)了玄柏。

綜上所述,我們可以知道閉包本質(zhì)上和函數(shù)一樣都是代碼塊贴铜,而閉包更加靈活粪摘。

閉包、嵌套函數(shù)绍坝、函數(shù)

更好地使用閉包前需要理清3者的聯(lián)系和區(qū)別

首先看3種函數(shù)的定義:

//函數(shù)
func eatTomatos(a: Int, b: Int) -> Int {
    return a + b
}

//嵌套函數(shù)
func eatTomatos(a: Int, b: Int) -> Int {
    //嵌套函數(shù)
    func digest(a: Int, b: Int) -> Int {
        return 2 * a + b
    }
    
    return digest(a: a, b: b)
}

//閉包
var eatTomatos = {(a: Int, b: Int) -> Int in
    return a + b
}

從上面的定義可以看出函數(shù)和嵌套函數(shù)其實是一回事徘意,唯一的區(qū)別是,嵌套函數(shù)是定義在一個函數(shù)內(nèi)部的函數(shù)轩褐,對外部是隱藏的椎咧,只能在其定義的函數(shù)內(nèi)部有效。而閉包與函數(shù)的不同要多一些:1把介、不需要使用func關鍵字勤讽,2、其次函數(shù)有名稱如:eatTomatos拗踢,而閉包是沒有名稱的脚牍,3、閉包的參數(shù)和函數(shù)體都要使用{ }包起來巢墅,在參數(shù)后要使用in關鍵字連接函數(shù)體诸狭,4、閉包可以作為一種類型賦值給一個變量君纫,上面代碼中的閉包類型是:(Int, Int) -> Int驯遇。

上面從定義上分析了3者的不同,下面從功能上區(qū)分下蓄髓。
1妹懒、函數(shù)是全局的,不能捕獲上下文中的變量双吆;而嵌套函數(shù)和閉包可以直接嵌套在上下文中使用的眨唬,因此可以捕獲上下文中的變量会前,需要注意的是每一個閉包都只會持有一個它所捕獲的變量的副本,如下:

override func viewDidLoad() {
        super.viewDidLoad()
        print(eatTomatos(a: 1, b: 2))//③
        print(eatTomatos(a: 2, b: 3))//④
}

func eatTomatos(a: Int, b: Int) -> Int {
    var numArray: Array<Int> = Array.init()
    
    //嵌套函數(shù)
    func digest(a: Int, b: Int) -> Int {
        numArray.append(a)
        numArray.append(b)
        print(numArray.count)//②
        return 2 * a + b
    }
    
    print(numArray.count)//①
    
    return digest(a: a, b: b)
}

//打印的結果依次(①②③④)是:
0
2
4
0
2
7

2匾竿、閉包可以作為參數(shù)或者返回值瓦宜,如下:

// 作為參數(shù)
override func viewDidLoad() {
    super.viewDidLoad()
    cookTomates { (a, b) in
        print(a)
        print(b)
    }
}

func cookTomates(tomato: (Int, Int) -> Void){
    tomato(1, 2)
}

cookTomates函數(shù)將閉包(Int, Int) -> Void作為參數(shù),并且可以在函數(shù)內(nèi)部操作這個閉包 在調(diào)用cookTomates函數(shù)式需要將給這個閉包參數(shù)賦值岭妖,并且閉包中的參數(shù)名需要調(diào)用的時候自行命名临庇。

//作為返回值
override func viewDidLoad() {
    super.viewDidLoad()
    
    let tomato = gainTomatos()
    print(tomato(2, 3))
    
}
var eatTomatos: (Int, Int) -> Int = {(a: Int, b: Int) -> Int in
    return a + b
}
func gainTomatos() -> (Int, Int) -> Int {
    return eatTomatos
}

函數(shù)gainTomatos將閉包(Int, Int) -> Int作為返回值,這里返回的是(Int, Int) -> Int的一個實例昵慌,調(diào)用者便可以利用返回的實例獲取(Int, Int) -> Int閉包處理參數(shù)的邏輯假夺,實現(xiàn)代碼的傳遞和復用

為你的閉包類型起別名

閉包類型不像其他常用類型看起來比較簡潔,有參數(shù)斋攀、返回值已卷、關鍵字、符號構成淳蔼,影響閱讀和糾錯侧蘸,因此為常用的閉包類型起一個別名很有必要。

如下鹉梨,為(Int, Int) -> Int閉包類型起別名

typealias Tomato = (Int, Int) -> Int

因此上面閉包當做返回值使用的代碼便可以改寫如下:

override func viewDidLoad() {
    super.viewDidLoad()
    
    let tomato = gainTomatos()
    print(tomato(2, 3))
    
}

var eatTomatos: Tomato = {(a: Int, b: Int) -> Int in
    return a + b
}

func gainTomatos() -> Tomato {
    return eatTomatos
}

當我們把(Int, Int) -> Int類型抽象為Tomato后讳癌,不僅僅是代碼看起來更加簡潔,也更接近我們使用的其他參數(shù)類型存皂,更加便于理解

閉包傳值

OC中常用的傳值方法有代理晌坤、Block、通知等旦袋,對應到Swift Block就由閉包替代骤菠。

如下需要使用閉包將B中的a、b值傳遞到A中

override func viewDidLoad() {
    super.viewDidLoad()
    
    let a: A = A()
    a.fromB()
    
}

typealias Tomato = (Int, Int) -> Int

class A: NSObject {
    let b: B = B()
    
    func fromB() {
        b.tomato = {
            (x, y) -> Int in
            return x + y
        }
        print(b.toA())
    }
    
}

class B: NSObject {
    var tomato: Tomato?
    
    func toA() -> Int {
        let a = 3
        let b = 4
        return tomato!(a, b)
    }
    
}

由上可以總結出閉包傳值的流程: 1??首先為自己的閉包類型起一個別名猜憎,便于使用娩怎; 2??在需要把值傳遞給另外一個對象的類里聲明一個閉包類型的變量搔课,對應到上面的代碼中就是B胰柑; 3??在需要接收值的類里為閉包類型賦值,從而在此閉包內(nèi)便可以獲取傳遞的值爬泥。
注意: 這里著重描述傳值的流程柬讨,在開發(fā)的時候還需判斷閉包是否為nil,否則會導致崩潰袍啡;

閉包作為參數(shù)傳值

在使用AFN或者SDWebImage的時候踩官,通過Block獲取請求的數(shù)據(jù)很方便,那么在Swift中如何使用閉包實現(xiàn)這種效果呢境输。

其實上面在說閉包作為參數(shù)使用的時候蔗牡,已經(jīng)實現(xiàn)了這種傳值的方式颖系,這里舉另外一個例子,我們在使用第三方庫的時候通常會將其再封裝一次辩越,避免由于第三方庫不維護或者出現(xiàn)較大更新的時候增加不必要的工作量嘁扼,這里以簡單封裝Alamofire為例,代碼如下:

import UIKit
import Alamofire
import SwiftyJSON

class ZYLResponse: NSObject {
    //接收數(shù)據(jù)是否成功
    var isSuccess: Bool = false
    //接收到的字典數(shù)據(jù)
    var dict: Dictionary<String, Any>?
    //接收到的數(shù)組數(shù)據(jù)
    var array: Array<Any>?
    //錯誤信息
    var error: Error?
    //JSON
    var json:JSON?
    
}

typealias DataReply = (ZYLResponse) -> Void

class ZYLNetTool: NSObject {
    
    ///POST請求
    open static func post(url: String, parameters: Dictionary<String, Any>?, complete: @escaping DataReply) {
        Alamofire.request(url, method: .post, parameters: parameters).responseJSON { (response) in
            let myResponse = ZYLResponse()
            myResponse.isSuccess = response.result.isSuccess
            myResponse.dict = response.result.value as! Dictionary<String, Any>?
            myResponse.array = response.result.value as? Array<Any>
            myResponse.error = response.result.error
            myResponse.json = JSON(data: response.data!)
            
            complete(myResponse)
        }
    }
    
    ///GET請求
    open static func get(url: String, parameters: Dictionary<String, Any>?, complete: @escaping DataReply) {
        Alamofire.request(url, method: .get, parameters: parameters).responseJSON { (response) in
            let myResponse = ZYLResponse()
            myResponse.isSuccess = response.result.isSuccess
            myResponse.dict = response.result.value as! Dictionary<String, Any>?
            myResponse.array = response.result.value as? Array<Any>
            myResponse.error = response.result.error
            myResponse.json = JSON(data: response.data!)
            
            complete(myResponse)
        }
    }
    
}

//調(diào)用
        ZYLNetTool.post(url: HTTP_ACTIVATE_PORT, parameters: paraDict, complete: { (response) in
            if response.isSuccess {
                //請求數(shù)據(jù)成功
               
            } else {
                //請求數(shù)據(jù)失敗

            }
        })

注意: 1黔攒、使用閉包時要注意管理內(nèi)存趁啸; 2、當作閉包為函數(shù)參數(shù)使用時可以脫離函數(shù)獨立使用時督惰,要將此閉包聲明為逃逸閉包不傅,在參數(shù)類型前面加上@escaping,否則會報錯赏胚。

尾巴

接觸一種新的事物之前總會覺得很難访娶,當我們學會后發(fā)現(xiàn)其實很簡單,難的不是這個新事物本身栅哀,而是我們的大腦出于習慣很難接受新的事物震肮,總是需要一定的過程。記得學C語言時很難理解指針留拾,學C++時很難理解面向?qū)ο蟠辽危瑢WOC時很難理解Block,而Swift作為一種新的語言痴柔,必然會有很多新的事物讓我們難以理解沦偎,比如閉包、元組咳蔚、可選類型豪嚎、函數(shù)式編程等等,本文只對閉包發(fā)表一點拙見谈火,還望指正侈询,謝謝。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末糯耍,一起剝皮案震驚了整個濱河市扔字,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌温技,老刑警劉巖革为,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異舵鳞,居然都是意外死亡震檩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門蜓堕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抛虏,“玉大人博其,你說我怎么就攤上這事∮睾铮” “怎么了贺奠?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長错忱。 經(jīng)常有香客問我儡率,道長,這世上最難降的妖魔是什么以清? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任儿普,我火速辦了婚禮,結果婚禮上掷倔,老公的妹妹穿的比我還像新娘眉孩。我一直安慰自己,他們只是感情好勒葱,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布浪汪。 她就那樣靜靜地躺著,像睡著了一般凛虽。 火紅的嫁衣襯著肌膚如雪死遭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天凯旋,我揣著相機與錄音呀潭,去河邊找鬼。 笑死至非,一個胖子當著我的面吹牛钠署,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播荒椭,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼谐鼎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了趣惠?” 一聲冷哼從身側(cè)響起狸棍,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎信卡,沒想到半個月后隔缀,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體题造,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡傍菇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了界赔。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丢习。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡牵触,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出咐低,到底是詐尸還是另有隱情揽思,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布见擦,位于F島的核電站钉汗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鲤屡。R本人自食惡果不足惜损痰,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酒来。 院中可真熱鬧卢未,春花似錦、人聲如沸堰汉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽翘鸭。三九已至滴铅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間就乓,已是汗流浹背失息。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留档址,地道東北人盹兢。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像守伸,于是被迫代替她去往敵國和親绎秒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

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