前面寫過一篇block的文章。swift看了看,閉包這塊感覺block差不多。代碼敲了敲,流程走一遍,有了一點理解。
1、閉包的簡單使用
PS:還是習(xí)慣用block命名閉包
let addBlock:(Int, Int) -> (Int) = {
(a, b) -> Int in
return a+b;
}
let c:Int = addBlock(10, 15);
print(c);
如上,定義一個閉包,閉包名稱是addBlock,閉包類型是:2個Int參數(shù),返回值是Int。后面緊接著就實現(xiàn)了閉包。下面就是閉包的調(diào)用,用一個Int類型接收閉包的返回值。over。
這么定義閉包的格式看著眼熟不?
let data:String = "我是網(wǎng)絡(luò)數(shù)據(jù)---哈哈"
沒錯,和定義一個普通的對象一樣,let 名稱:類型 = 結(jié)果,
閉包就是一個函數(shù)的類型。類型就是什么參數(shù)返回神結(jié)果
比如這個閉包類型就是2個Int參數(shù)返回一個Int結(jié)果。
這就是閉包的簡單使用。至于其他的書寫方式,在什么情況下,啥時候省略void,省略in,省略形參啥的,這里不做討論。萬變不離其宗,都是這個格式的變形而已。
再看一個swift函數(shù)的定義和使用
//函數(shù)的聲明、實現(xiàn)
func stadFunc(pa:Int, pb:Int) -> Int{
return pa + pb;
}
//調(diào)用函數(shù)
let d = stadFunc(pa: 10, pb: 3);
print(d);
和閉包一樣,同樣的定義,參數(shù)、返回值、實現(xiàn)的大括號部分等等。
以前block的時候就說過block其實就是函數(shù)的變形,閉包同理。
如果閉包都這么簡單的使用,和普通函數(shù)沒啥區(qū)別,就體現(xiàn)不出它的價值了。
2、閉包的回調(diào)
以前block就說回調(diào)的功能,這個功能才是主要的用途,可以傳遞數(shù)據(jù)、發(fā)送個通知啥的。
上面說過,閉包可以看作函數(shù),具有這個函數(shù)的類型。既然有了類型,就和普通數(shù)據(jù)一樣,可以當(dāng)做另外一個函數(shù)的參數(shù)。
func textAddBlock(block:(Int, Int) -> Void, failBlock:()->Void){
let resultA = 11;
let resultB = 12;
print("打印1")
block(resultA, resultB)
}
textAddBlock(block: { (pa:Int, pb:Int) in
let result = pa + pb;
print("block結(jié)果打印\(result)")
}) {
print("failBlock打印2222")
};
上面的代碼,我定義了2個閉包,當(dāng)做參數(shù)傳遞給另外一個函數(shù)了。
類似 func stadFunc(pa:Int, pb:Int) -> Int 函數(shù)中傳了2個Int類型的參數(shù)。
既然強調(diào)了幾遍閉包就是函數(shù)的概念。那么閉包使用起來和函數(shù)一樣,實現(xiàn)和調(diào)用一個都不少。
回調(diào)的巧妙之處在于,一個函數(shù)是另一個函數(shù)的參數(shù),閉包作為參數(shù),就需要被另外一個函數(shù)使用,就像a:Int參數(shù),在函數(shù)的使用部分用起來可以是return a;。閉包使用起來,就是調(diào)用它了。(參考上面代碼)
實現(xiàn)的代碼在另一個函數(shù)的調(diào)用部分,所以可以把這個函數(shù)實現(xiàn)部分的數(shù)據(jù)傳遞過來。就實現(xiàn)了回調(diào)。
所以回調(diào)的本質(zhì)就是兩個函數(shù)的聲明實現(xiàn)代碼和調(diào)用代碼相互嵌套書寫。
3、逃逸閉包
簡單來說,就是一個閉包的調(diào)用的時候,它所在的函數(shù)實現(xiàn)部分已經(jīng)結(jié)束了。常用于網(wǎng)絡(luò)請求等多線程中。
比如,我寫了個簡單網(wǎng)絡(luò)請求工具(什么都是假的)
import UIKit
class NetWokrTool: NSObject {
func requestResult(successBlock:@escaping (String, Bool)->Void, failBlokc:@escaping (Bool)->Void) -> Void {
DispatchQueue.global().async {
sleep(2);
let data = "我是網(wǎng)絡(luò)數(shù)據(jù)---哈哈"
let result:Bool = true;
successBlock(data, result);
failBlokc(result);
};
}
}
定義實現(xiàn)了一個請求的方法,其中把2個閉包作為參數(shù),一個成功,一個失敗的閉包。模擬了子線程請求。
得到了網(wǎng)絡(luò)數(shù)據(jù)和請求結(jié)果后,通過調(diào)用閉包,把數(shù)據(jù)傳遞出去。
由于需要在控制器中的主線程中調(diào)用這個工具類,正常走下去,走到這個方法里,發(fā)現(xiàn)有了子線程,主線程不管這個,繼續(xù)走,直到這個函數(shù)走完,函數(shù)的生命周期就結(jié)束了。但是函數(shù)走完了,子線程還在工作,里面的代碼還沒結(jié)束,導(dǎo)致閉包的生命周期比這個函數(shù)還長,不會在相關(guān)函數(shù)結(jié)束后釋放。所以需要逃出這個函數(shù)的掌控才能工作,用@escaping標(biāo)記為逃逸閉包。其實是為了內(nèi)存的釋放。(感覺類似block的弱引用的作用差不多)
在控制器中調(diào)用這個工具類
let tool:NetWokrTool = NetWokrTool();
tool.requestResult(successBlock: { (data, result) in
print(data,result)
}) { (result) in
print(result);
};
結(jié)果:

最終把請求得到的數(shù)據(jù)和結(jié)果回調(diào)到了控制器中。