*****閱讀完此文,大概需要5分鐘******
一、閉包的基本概念與寫法
1、Swift中閉包需要對比OC的block
- OC的基本定義與寫法:
回傳值(^閉包名稱)(引數型別 引數名) = ^(引數型別 引數名){函式體};
void(^XXBlock)(int a) = ^(int a){
NSLog(@"hello");
};
- Swift基本定義與寫法:
{(引數:引數型別)->回傳值型別 in 函式體}如果設定了回傳值,記得回傳對應型別的值
let test = {(a:Int)->Int in
return a;
};
個人感覺Swift的閉包寫法較為精簡并且好記,如果手寫,相信很多人應該深有感觸,面試時手寫block簡直是噩夢,相比較而言,Swift的閉包更容易手寫出來,
2、Swift的閉包實體應用
- 閉包作為屬性的實體應用:
// KXRequest.swift
typealias RequestBlock = (_ name:String)->Void;
var block : RequestBlock?;
func show() {
self.block?("kx")
}
// ViewController.swift
let request = KXRequest();
request.block = { (name:String) in
print(name)
};
request.show()
- 閉包作為函式引數
func requestInfo(complection:((Bool, Array<String>) -> Void)? = nil) {
complection?(false, [])
}
self.requestInfo { [weal self] (sucess, result) in
guard let self = self else {return}
If !sucess {
return
}
print(result)
}
二、后置閉包、自動閉包、逃逸閉包等
1、后置閉包
又稱尾隨閉包,是一個書寫在函式括號之后的閉包運算式,函式支持將其作為最后一個引數呼叫,
let names = ["AT", "AE", "D", "S", "BE"]
var reversed = names.sorted() { $0 > $1 }
print(reversed)
- 單個后置閉包的應用1:
//單個閉包
UIView.animate(withDuration: 0.3, animations: {() in
})
- 單個后置閉包的應用2:
class MessageContentModel: AnyObject {
var timestamp: TimeInterval = 0
var identifier: String = "MessageModel"
…
}
let sortedMessageModels = messageModels.sorted(by: {
$0.timestamp < $1.timestamp
})
- 多個后置閉包應用1:
//多個閉包
UIView.animate(withDuration: 0.3. animations: { () in
// …
}, completion: { (finish) in
// ….
})
- 多個后置閉包應用2
let request = ABRequest(info)
request.startWithCompletionBlock { [weak self] (req) in
guard let self = self, let request = req as? ABRequest else {return}
self.handleResponce(req: request, userId: info.userId)
} failure: { [weak self] (req) in
guard let self = self, let request = req as? ABRequest else {return}
self.handleResponce(req: request, userId: info.userId)
}
//或者
let request = ABRequest(info)
request.startWithCompletionBlock(success: { [weak self] (req) in
guard let self = self, let request = req as? ABRequest else {return}
self.handleResponce(req: request, userId: info.userId)
}) { [weak self] (req) in
guard let self = self, let request = req as? ABRequest else {return}
self.handleResponce(req: request, userId: info.userId)
}
2、自動閉包@autoclosure
autoclosure顧名思義,這是一種可以“自動”的Closure,即可以讓運算式的型別轉為相應的Closure的型別,在原本的呼叫處,可以省略Closure引數的大括號,其只可以修飾作為引數的 Closure,但該 Closure 必須為無參,回傳值可有可無,
func kClosureExample(_ compression: () -> Bool) {
if compression() {
print("true")
}
}
kClosureExample { 1 < 2 }
func kAutoClosureExample (_ compression: @autoclosure () -> Bool) {
if compression() {
print("true")
}
}
kAutoClosureExample( 1 < 2 )
3、逃逸閉包與非逃逸閉包
Swift 3.0之后,傳遞閉包到函式中的時候,系統會默認為非逃逸閉包型別@noescaping,逃逸閉包在閉包前要添加@escaping關鍵字,它們是根據閉包的呼叫時的時機劃分的,
- 非逃逸閉包
不需要其它特殊時機,只能在函式作用域內函式執行結束前被呼叫,非逃逸閉包的生命周期與函式相同:
A. 把閉包作為引數傳給函式,
B. 函式中呼叫閉包,
C. 退出函式,結束,
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.myTest {
print("非逃逸(noescaping)")
}
}
func myTest(callback:()->()) {
print("before")
callback()
print("after")
}
}
- 逃逸閉包
不需要在函式結束前被呼叫,可以等到特定時機時才被呼叫,逃逸閉包的生命周期:
A. 閉包作為引數傳遞給函式,
B. 退出函式,
C. 閉包被呼叫,閉包生命周期結束
class ViewController: UIViewController {
var escapingCallback : (()->())?
override func viewDidLoad() {
super.viewDidLoad()
self.myTest {
print("逃逸(escaping)")
}
escapingCallback?()
}
func myTest(callback:@escaping ()->()) {
escapingCallback = callback
}
}
給當前類宣告一個閉包變數escapingCallback,將myTest函式內的閉包引數賦值給這個變數,此時這個閉包就可以在函式執行結束后被呼叫,
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.myTest{
print("逃逸(escaping)")
}
}
func myTest(callback:@escaping()->()) {
print("before”)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
callback()
}
print("after")
}
}
不需要在myTest函式結束前呼叫,等到1秒后再呼叫閉包callback,
三、閉包中的回圈參考與解決方法
類似OC的Block,回圈參考的方式有三種:
- 使用weak修飾變數, 打破強參考,實體代碼:
weak var weakSelf = self
loadData { (textString) -> () in
print("\(textString) \(weakSelf?.view)")
}
- 使用[weak self] 修飾閉包原理跟__weak類似, 這樣在閉包中使用self, 就是弱參考
// [weak self]
loadData { [weak self] (textString) -> () in
//閉包中的self 都是弱參考的
print("\(textString) \(self?.view)")
}
- 使用[unowned self ] 修飾閉包, 跟__unsafe_unretained類似, 慎用
loadData { [unowned self] (dataString) -> () in
print("\(dataString) \(self.view)")
}
- [weak self] 與__weak,[unowned self ]與__unsafe_unretained
__weak 表示的是物件的弱參考關系,__weak修飾的物件被釋放后,指向物件的指標會自動置為空,也就是指向nil,__unsafe_unretained表示的是弱參考關系,__unsafe_unretained修飾的物件被釋放后,指標不會置為空,變成一個野指標,如果后續再訪問這個物件可能就會crash,所以[unowned self ]與__unsafe_unretained都要慎用,[unowned self ]與__unsafe_unretained解除回圈參考,不被釋放, 一般必須是自己的實體屬性,
四、與OC的block的不同
上面可以看出OC的block與Swift的閉包基本一致,但是它們也有區別的地方,
- Swift的閉包和OC的Block里值的捕獲的區別
OC代碼捕獲實體:
NSInteger a = 100;
void(^block)(void) = ^{
NSLog(@"block = %ld:", a);
};
a += 1;
NSLog(@"out1 = %ld:", a);
block();
NSLog(@"out2 = %ld:", a);
結果列印如下:
2021-08-17 11:27:13.846743+0800 MDProject[30746:23593763] out1 = 101
2021-08-17 11:27:13.846885+0800 MDProject[30746:23593763] block = 100
2021-08-17 11:27:13.847002+0800 MDProject[30746:23593763] out2 = 101
在OC編譯器走到第三行的時候,實際上已經完成了對a的拷貝,
Swift代碼捕獲實體:
var a = 100
let closure = {
print("closure = \(a)")
}
a += 1
print("out 1 = \(a)")
closure()
print("out 2 = \(a)")
結果列印如下:
out1 = 101
closure = 101
out 2 = 101
Swift閉包都是捕獲的是“參考”,而不是他們參考的物件,
- 閉包中修改值:
var a = 100
let closure = {
a += 1
print("closure = \(a)")
}
a += 1
print("out 1 = \(a)")
closure()
print("out 2 = \(a)")
在閉包里多了一行 a += 1 編譯,沒有警告,運行結果如下:
out 1 = 101
closure = 102
out 2 = 102
也就是說,在Swift里,閉包就像是oc給外部變數默認添加了__block或者__weak
- Swift里的捕獲串列(capturing list)
Swift里實作和OC一樣的值捕獲,看如下代碼:
var a = 100
let closure = {
[a] in
print("closure = \(a)")
}
a += 1
print("out 1 = \(a)")
closure()
print("out 2 = \(a)")
閉包內部多了一行[a] in 語法為中括號[]里面添加捕獲的變數,然后用in 分割上下分,結果列印如下:
out 1 = 101
closure = 100
out 2 = 101
五、參考檔案
以上部分片段摘錄自以下檔案:
https://www.jianshu.com/p/33707bd27500
https://www.jianshu.com/p/61ee76234357
https://blog.csdn.net/qq_30932479/article/details/80517518
https://www.jianshu.com/p/cbd0b7390bcf
https://www.jianshu.com/p/65d0d6a1ced1
https://www.dazhuanlan.com/abiosis/topics/1242173
https://www.runoob.com/swift/swift-closures.html
感謝作者們的分享,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/294821.html
標籤:其他
上一篇:學校帶學院結尾,是我的鍋嗎?但就算如此,二本渣院的我還是拿到了阿里的Offer!
下一篇:資訊搜集之nmap
