Swift Closures隨想

本文不會對Closures的基本語法進行解讀,如果對于Closures語法有所疑惑,請參考Apple官方文檔
本文主要針對Closures在項目中的使用情況進行分析,主要涉及為何使用、何時使用、如何使用等幾個方面進行分析
如有不足之處歡迎批評指正,謝謝!

In programming languages, closures (also called lexical closures or function closures) are techniques for implementing lexically scoped named binding in languages with first-class functions. Operationally, a closure is a record storing a function together with an environment: a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.?—?Wikipedia

是啥

官方文檔給出這樣一句話:Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.
大意就是:閉包是一個獨立的功能塊,可以在代碼中傳遞和使用。 Swift中的閉包類似于C和Objective-C中的block以及其他編程語言中的lambdas。
說白了和函數(shù)比較類似,對它們的調(diào)用本質(zhì)上都是執(zhí)行了一段代碼,它們都可以有參數(shù),都可以有返回值,只是表現(xiàn)的方式不同。Closures有它自己的特色。

為啥要用

說白了,用Closures就是為了方便、簡潔、優(yōu)雅。
其實Closures并沒有賦予我們額外的能力,可以用Closures實現(xiàn)的功能,也可以用其他方法來實現(xiàn)。不過Closures確實可以使代碼更加的清晰、緊湊以及可讀,眾所周知,清晰明了的代碼會包含更少的bug,測試起來也更加方便。
Closures只是一種讓函數(shù)訪問本地狀態(tài)、變量等更加方便的途徑而已。
我們不必創(chuàng)建一個可以使用局部變量的類,而只需在現(xiàn)場定義函數(shù),它就可以隱式訪問當前可見的每個變量。使用傳統(tǒng)OOP語言定義成員方法時,從某種意義上來說,這些成員方法就相當于閉包,因為它們可以訪問“此類中可見的所有成員”。

啥時候用

在具體的項目中,Closures主要用于回調(diào)以及代理中。
例如,讓你設(shè)計一個網(wǎng)絡(luò)請求的類,你如何把請求的結(jié)果返回給調(diào)用者?因為網(wǎng)絡(luò)請求是一個異步的過程,你不能直接通過函數(shù)的返回值直接傳遞結(jié)果,用戶也不可能一直等著函數(shù)返回,那么該怎么辦呢?
一個辦法是使用通知。當請求結(jié)束時,被調(diào)用方會發(fā)送網(wǎng)絡(luò)請求結(jié)束通知,帶上網(wǎng)絡(luò)請求的結(jié)果,這種方法需要你注冊成為觀察者,然后編寫收到通知后要執(zhí)行的代碼。試想一下,我們?nèi)绻衝多個網(wǎng)絡(luò)請求,就需要在結(jié)果返回時去判斷該結(jié)果屬于哪一個網(wǎng)絡(luò)請求,然后才能對結(jié)果進行相應(yīng)處理。一種方法是使用代理,實現(xiàn)網(wǎng)絡(luò)請求的代理方法,網(wǎng)絡(luò)請求結(jié)束時會調(diào)用代理方法,傳遞網(wǎng)絡(luò)請求結(jié)果,這種方法和上面相比少了一步注冊成為觀察者的過程,但是同樣需要在代理方法中對結(jié)果進行判斷。這兩種方法都需要將請求和結(jié)果分開來寫,閱讀起來不是很直觀。
另一種方法就是使用Closures。既然我們不知道網(wǎng)絡(luò)請求何時返回結(jié)果,但是我們知道請求返回時我們要做什么,也就是說網(wǎng)絡(luò)請求結(jié)束后我們一定會進行這些操作,那么我們有沒有什么辦法直接告訴被調(diào)方,在你請求網(wǎng)絡(luò)結(jié)束后,進行我需要的操作。說白了就是你跑完你的代碼以后,能不能接著跑我的代碼,這個問題的關(guān)鍵在于怎么能讓對方知道我要讓它跑什么代碼,怎么能讓它知道呢?當然是直接告訴它啊,也就是直接把你想執(zhí)行的代碼告訴它,這種‘告訴’可以通過參數(shù)傳遞過去。什么?把要執(zhí)行的方法傳過去?沒錯,就是直接傳過去,在C語言中我們可以直接傳遞一個函數(shù)指針,OC中可以使用selector或者block,Swift理所當然就是用Closures了。什么?你沒聽懂?沒關(guān)系,時間久了,用的多了自然而然就差不多了。
Closures的使用時機需要具體問題具體分析,這就需要有比較豐富經(jīng)驗,不能單純?yōu)榱耸褂枚褂?,有時候過多的使用Closures會使代碼更加難懂,比如說Closures嵌套Closures的情況。

怎么用

OC中block:

//As a local variable:
returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};
//As a property:
@property (nonatomic, copy, nullability) returnType (^blockName)(parameterTypes);
//As a method parameter:
- (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;
//As an argument to a method call:
[someObject someMethodThatTakesABlock:^returnType (parameters) {...}];
//As a parameter to a C function:
void SomeFunctionThatTakesABlock(returnType (^blockName)(parameterTypes));
//As a typedef:
typedef returnType (^TypeName)(parameterTypes);
TypeName blockName = ^returnType(parameters) {...};

Swift Closures:

//As a variable:
var closureName: (ParameterTypes) -> ReturnType
//As an optional variable:
var closureName: ((ParameterTypes) -> ReturnType)?
//As a type alias:
typealias ClosureType = (ParameterTypes) -> ReturnType
//As a constant:
let closureName: ClosureType = { ... }
//As a parameter to another function:
funcName(parameter: (ParameterTypes) -> ReturnType)
//Note: if the passed-in closure is going to outlive the scope of the method, e.g. if you are saving it to a property, it needs to be annotated with @escaping.

//As an argument to a function call:
funcName({ (ParameterTypes) -> ReturnType in statements })
//As a function parameter:
array.sorted(by: { (item1: Int, item2: Int) -> Bool in return item1 < item2 })
//As a function parameter with implied types:
array.sorted(by: { (item1, item2) -> Bool in return item1 < item2 })
//As a function parameter with implied return type:
array.sorted(by: { (item1, item2) in return item1 < item2 })
//As the last function parameter:
array.sorted { (item1, item2) in return item1 < item2 }
//As the last parameter, using shorthand argument names:
array.sorted { return $0 < $1 }
//As the last parameter, with an implied return value:
array.sorted { $0 < $1 }
//As the last parameter, as a reference to an existing function:
array.sorted(by: <)
//As a function parameter with explicit capture semantics:
array.sorted(by: { [unowned self] (item1: Int, item2: Int) -> Bool in return item1 < item2 })
//As a function parameter with explicit capture semantics and inferred parameters / return type:
array.sorted(by: { [unowned self] in return $0 < $1 })

OC Block和Swift Closures之間的語法差別從上面可以清晰的看出了,在這里就不總結(jié)了。

下面說一說我認為比較有意思的東西

對于調(diào)用方,Closures中的參數(shù)是被調(diào)方傳遞給你的,如果被調(diào)方傳給你的參數(shù)還是Closures,那么這個Closures其實已經(jīng)是被調(diào)方實現(xiàn)好了的,只需要你傳遞Closures所需的參數(shù)即可。下面通過例子簡單看一下

typealias closure1 = (_ intValue: Int) -> Void
typealias closure2 = (_ closure: closure1) -> Void
typealias closure3 = (_ closure: closure2) -> Void

定義了三個Closures,closure1有一個Int類型參數(shù),closure2有一個closure1類型參數(shù),closure3有一個closure2類型參數(shù)。

func testClosure1(closure1: closure1) -> Void {
    //do something...
    if let closure1 = closure1 {
        closure1(1)
    }
}

testClosure1 { (i) in
    print(i)
}

上面這種使用方法在開發(fā)過程中應(yīng)該會經(jīng)常用到,通常是用作回調(diào),需要注意的是這里closure1中的參數(shù)是被調(diào)用方傳入的,調(diào)用方只需要傳入具體實現(xiàn)就行,也就是說,被調(diào)方用自己傳入自己的參數(shù)調(diào)用你的實現(xiàn),就是通常意義上的回調(diào),雙方約定好參數(shù)類型,被調(diào)方提供參數(shù),調(diào)用方提供實現(xiàn)。

func testClosure2(closure2: closure2) -> Void {
    var closure1: closure1 = { i in
        print(i)
    }
    closure2(closure1)

    //或者使用以下寫法
    closure2 { i in //這其實是block1的具體實現(xiàn),參數(shù)類型是int
        print(i)
    }
}

testClosure2 { (closure1) in
    closure1(5)
}

上面這種方式開發(fā)中也會用到,closure2中的參數(shù)是closure1,此時被調(diào)用方傳入的參數(shù)其實是類型為closure1的參數(shù),這是調(diào)用方拿到的就是closure1這個closure,這個closure已經(jīng)由被調(diào)用方定義好了,傳入?yún)?shù)即可使用。(可能有點繞,內(nèi)部提供實現(xiàn),外部提供參數(shù))
下面這個可能更繞

func testClosure3(closure3: closure3) -> Void {
    var closure22: closure2 = { closure in
        closure(11)
    }
    closure3(closure22)
}
testClosure3 { closure in
    closure({ i in
        print(i)
    })
}

3重closure,這是就需要調(diào)用方傳入closure1的具體實現(xiàn),被調(diào)方在此基礎(chǔ)上傳入?yún)?shù)實現(xiàn)closure2,最后再將這個closure2當作參數(shù)傳遞出去,日常開發(fā)中反正我是沒用過,如果大家發(fā)現(xiàn)有這么用的那么請告訴我,讓我也一起學習下。至于更多重的closures,請大家自行腦補吧。

To Be Continued

說白了不論是Closure還是其他的語言特性,都是為了更好的與機器交流而已,編程語言使我們可以與機器交流,像中文英文可以讓我們相互交流而已。而在編程語言的特性,就好比某些網(wǎng)絡(luò)通用語或者諺語,往往可以使用很簡短的幾句代碼或幾句話,實現(xiàn)很復雜的功能。這也是編程語言發(fā)展的一個趨勢。

如有錯誤請批評指正,謝謝!

最后編輯于
?著作權(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)容

  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學習記錄文檔,今天18年5月份再次想寫文章,發(fā)現(xiàn)簡書還為我保存起的...
    Jenaral閱讀 3,179評論 2 9
  • 以下翻譯自Apple官方文檔,結(jié)合自己的理解記錄下來。翻譯基于 swift 3.0.1 原文地址 Closure...
    藝術(shù)農(nóng)閱讀 1,720評論 0 3
  • 點擊查看原文 Web SDK 開發(fā)手冊 SDK 概述 網(wǎng)易云信 SDK 為 Web 應(yīng)用提供一個完善的 IM 系統(tǒng)...
    layjoy閱讀 14,467評論 0 15
  • 在生活中,我們每個人對快樂的定義不同,比如我們大多數(shù)人追求的是富足美滿的物質(zhì)生活,然而還有一小部分則追求的是精神上...
    春風相對閱讀 446評論 0 1
  • 今天是來高博學習的第32天。 bootstrap下拉菜單,怎樣讓鼠標移入下拉菜單顯示出來,移出時下拉菜單隱藏? 默...
    RyuukoGYC閱讀 199評論 1 0

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