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