"閉包,Swift中的新概念,然而除了寫法不同,實(shí)際上用法與用途都和OC中的Block沒什么不同"
閉包
Swift中并沒有block的概念,但為了取代block,Swift中提出了另一個(gè)概念"閉包"(我猜它的英文名應(yīng)該叫B-Box吧...童言無忌~)
Tips:
閉包經(jīng)常用于回調(diào),本質(zhì)也是一個(gè)代碼塊
閉包的使用方法:
- 閉包的寫法:
- 對(duì)比OC中的block,我們知道閉包的完整用法有三步:
1.閉包的定義:
var 閉包名:(形參列表)->(返回值)
2.閉包的實(shí)現(xiàn):
閉包名 = {
(形參) -> 返回值類型 in
// 執(zhí)行代碼
}
3.閉包的回調(diào):
閉包名(形參)
- 我們?cè)谶@里模擬一個(gè)場(chǎng)景:創(chuàng)建一個(gè)網(wǎng)絡(luò)請(qǐng)求工具類,在子線程中請(qǐng)求數(shù)據(jù),在主線程中刷新UI,以此為例子來看一下閉包的使用方法
例:模擬網(wǎng)絡(luò)請(qǐng)求工具類
//創(chuàng)建網(wǎng)絡(luò)請(qǐng)求工具類
class HttpTool {
func loadData(completeHandle : () -> ()) { //1.第一步:定義發(fā)送網(wǎng)絡(luò)請(qǐng)求的方法,傳入一個(gè)"請(qǐng)求完成后回調(diào)的閉包"(這一步相當(dāng)于定義了一個(gè)() -> ()類型的閉包)
dispatch_async(dispatch_get_global_queue(0, 0)) { //跳轉(zhuǎn)到子線程
print("請(qǐng)求數(shù)據(jù) -> \(NSThread.currentThread())") //模擬發(fā)送網(wǎng)絡(luò)請(qǐng)求,同時(shí)打印當(dāng)前所在線程
dispatch_sync(dispatch_get_main_queue(), { //跳轉(zhuǎn)到主線程
completeHandle() //3.第三步:在主線程執(zhí)行回調(diào)
})
}
}
}
//外部調(diào)用
let tool = HttpTool()
tool.loadData({() -> (Void) in //2.第二步:實(shí)現(xiàn)閉包中的方法
print("刷新UI -> \(NSThread.currentThread())")
})
//打印結(jié)果
請(qǐng)求數(shù)據(jù) -> <NSThread: 0x7fe162c26a40>{number = 2, name = (null)}
刷新UI -> <NSThread: 0x7fe162d01730>{number = 1, name = main}
-
閉包實(shí)現(xiàn)的簡(jiǎn)便寫法
如果閉包沒有返回值也沒有參數(shù),那么可以省略掉:"() -> () in",那么上面例子中的"外部調(diào)用"部分可以簡(jiǎn)寫為如下:
let tool = HttpTool()
tool.loadData({ //省略掉了() -> (Void) in
print("刷新UI -> \(NSThread.currentThread())")
})
- 閉包的超簡(jiǎn)便寫法(尾隨閉包):
尾行尾隨閉包顧名思義,當(dāng)我們的閉包是整個(gè)函數(shù)中的最后一個(gè)參數(shù)的時(shí)候可以把整個(gè)閉包(整個(gè)大括{}號(hào)中的部分)拿出來放到()后面,那我們繼續(xù)簡(jiǎn)化例子中"外部調(diào)用"的部分
tool.loadData(){ //注意:此時(shí)閉包作為函數(shù)的最后一個(gè)參數(shù),被拿到了參數(shù)列表外部,并且緊跟在()后面
print("刷新UI -> \(NSThread.currentThread())")
}
尾隨閉包的進(jìn)一步簡(jiǎn)化:當(dāng)函數(shù)中只有一個(gè)參數(shù)的時(shí)候,并且恰好這個(gè)參數(shù)是閉包的時(shí)候,小括號(hào)()可以不寫!在追求極簡(jiǎn)的Swift語(yǔ)言中,這種最簡(jiǎn)化的閉包寫法,無疑是apple大力推薦的,如下:
let tool = HttpTool()
tool.loadData{ //注意:函數(shù)的參數(shù)列表()也可以不寫了哦
print("刷新UI -> \(NSThread.currentThread())")
}
Swift中快速解決閉包引發(fā)的循環(huán)引用:
- block引發(fā)的循環(huán)引用
block會(huì)對(duì)內(nèi)部的對(duì)象進(jìn)行強(qiáng)引用,我們拿當(dāng)前控制器的self來舉例:- 當(dāng)前控制器中定義一個(gè)httpTool類型的屬性tool,并將其實(shí)例化,則控制器強(qiáng)引用了tool
self.tool = HttpTool() //self強(qiáng)引用tool
- 如果tool中定義閉包屬性,則tool強(qiáng)引用該閉包
class HttpTool {
var completeHandle : (() -> ())?
func changeViewColor() -> Void {
completeHandle!()
}
}
- 如果閉包內(nèi)部調(diào)用了self的某一些方法,則閉包強(qiáng)引用了self,
self.tool!.completeHandle = {
self.view.backgroundColor = UIColor.redColor()
}
以上造成了循環(huán)引用
- 解決循環(huán)引用的辦法:
- weakSelf
在OC中,我們常常定義一個(gè)變量來弱引用self,以此來代替self在閉包中使用
- weakSelf
self.tool = HttpTool()
weak var wSelf = self //這里的wSelf是一個(gè)可選類型,因?yàn)閟elf被釋放后為nil,所以wSelf有可能指向nil
self.tool!.completeHandle = {
wSelf!.view.backgroundColor = UIColor.redColor()
}
self.tool?.changeViewColor()
- [weak self]
Swift中特有的方法,這種寫法會(huì)讓閉包內(nèi)所有的self都變?yōu)槿跻?/li>
self.tool = HttpTool()
self.tool!.completeHandle = {[weak self]() -> () in
self!.view.backgroundColor = UIColor.redColor() //這里的self為可選類型,有可能指向空(當(dāng)其引用的控制器被釋放時(shí),改變指向?yàn)閚il)
}
self.tool?.changeViewColor()
- [unowned self]
Swift中特有的方法,這種寫法會(huì)讓閉包內(nèi)所有的self都變?yōu)槿跻?與[weak self]不同的是,unowned引用的self所指向的實(shí)例被銷毀后,仍然會(huì)指向原有的存儲(chǔ)空間,所以這時(shí)閉包里的self既不是optional類型,也不可以指向nil
self.tool = HttpTool()
self.tool!.completeHandle = {[unowned self]() -> () in
self.view.backgroundColor = UIColor.redColor() //這里的self不是可選類型,故不需要強(qiáng)制解包
}
self.tool?.changeViewColor()