OC Block 和 Swift 閉包相互調(diào)用
我們在 OC
中定義的 Block
雁乡,在 Swift
中是如何調(diào)用的呢橄镜?我們來看一下
typedef void(^ResultBlock)(NSError *error);
@interface LGTest : NSObject
+ (void)testBlockCall:(ResultBlock)block;
@end
在 Swift
中我們可以這么使用
LGTest.testBlockCall{ error in
let errorcast = error as NSError
print(errorcast)
}
func test(_ block: ResultBlock){
let error = NSError.init(domain: NSURLErrorDomain, code: 400, userIn: nil)
block(error)
}
比如我們在 Swift
里這么定義,在 OC
中也是可以使用的
class LGTeacher: NSObject{
@objc static var closure: (() -> ())?
}
+ (void)test{
// LGTeacher.test{}
LGTeacher.closure = ^{
NSLog(@"end");
};
}
LGTest.test()
LGTeacher.closure!()
@convention 關(guān)鍵字介紹
@convention
:用于修飾函數(shù)類型
- 修飾
Swift
中的函數(shù)類型(調(diào)用C
函數(shù)的時候) - 調(diào)用
OC
方法時剂癌,修飾Swift
函數(shù)類型
defer 的用法
定義:defer {}
里的代碼會在當前代碼塊返回的時候執(zhí)行麦乞,無論當前代碼塊是從哪個分支 return
的,即使程序拋出錯誤十嘿,也會執(zhí)行。
- 案例 1
如果多個 defer
語句出現(xiàn)在同一作用域中岳锁,則它們出現(xiàn)的順序與它們執(zhí)行的順序相反绩衷,也就是先出現(xiàn)的后執(zhí)行。
- 案例 2
func append(string: String, terminator: String = "\n", toFileAt url: URL) throws {
// The data to be added to the file
let data = (string + terminator).data(using: .utf8)!
let fileHandle = try FileHandle(forUpdating: url)
defer {
fileHandle.closeFile()
}
// If file doesn't exist, create it
guard FileManager.default.fileExists(atPath: url.path) else {
try data.write(to: url)
return
}
// If file already exists, append to it
fileHandle.seekToEndOfFile()
fileHandle.write(data)
}
let url = URL(fileURLWithPath: NSHomeDirectory() + "/Desktop/swift.txt")
try append(string: "iOS開發(fā)語言", toFileAt: url)
try append(string: "Swift", toFileAt: url)
這里有時候如果當前方法中多次出現(xiàn) closeFile
激率,那么我們就可以使用 defer
咳燕。
- 案例 3
let count = 2
let pointer = UnsafeMutablePointer<Int>.allocate(capacity: count)
pointer.initialize(repeating: 0, count: count)
defer {
pointer.deinitialize(count: count)
pointer.deallocate()
}
在我們使用指針的時候,allocate
跟 deallocate
乒躺,initialize
跟 deinitialize
都是成對出現(xiàn)的招盲,這時候我們就可以將 deallocate
跟 deinitialize
放到 defer {}
中。
- 案例 4
func netRquest(completion: () -> Void) {
defer {
self.isLoading = false
completion()
}
guard error == nil else { return }
}
比如我們在進行網(wǎng)絡請求的時候嘉冒,可能有不同的分支進行回調(diào)函數(shù)的執(zhí)行曹货,這個時候可以使用 defer {}
來管理 completion
回調(diào),前提是這里是非異步的讳推,defer {}
就是方便我們來管理代碼塊控乾。
defer 使用注意
使用 defer
的時候要避免像這樣書寫,編譯器也會提示 defer
可能不會立即執(zhí)行娜遵。
這里需要注意的是 temp
的值還是 1,因為 defer {}
是在返回之后執(zhí)行壤短。其實這里 defer
這樣使用并沒有意義设拟,我們要避免這樣的寫法,defer
我們要用來管理一些統(tǒng)一資源久脯, 優(yōu)化冗余代碼纳胧, 使代碼更加簡潔直觀。
逃逸閉包
逃逸閉包:當閉包作為一個實際參數(shù)傳遞給一個函數(shù)的時候帘撰,并且是在函數(shù)返回之后調(diào)用跑慕,我們就說這個閉包逃逸了。當我們聲明一個接受閉包作為形式參數(shù)的函數(shù)時摧找,你可以在形式參數(shù)前加上 @escaping
來明確閉包是允許逃逸的核行。
- 作為函數(shù)的參數(shù)傳遞
- 當前閉包在函數(shù)內(nèi)部異步執(zhí)行或者被存儲
- 函數(shù)結(jié)束,閉包被調(diào)用蹬耘,生命周期結(jié)束
注意:可選類型默認是逃逸閉包芝雪!
- 使用場景 1
class CXTeacher {
var completionHandle: ((Int) -> Void)?
func makeIncrementer(_ amout: Int, handler: @escaping (Int) -> Void) {
var count = 10
count += amout
completionHandle = handler
}
func dosomething() {
makeIncrementer(20) {
print($0)
}
}
}
let t = CXTeacher()
t.dosomething()
t.completionHandle?(10)
這里是定義了一個 completionHandle
屬性,在 makeIncrementer
函數(shù)中將閉包參數(shù) handler
賦值給 completionHandle
综苔,這時候閉包的生命周期比 makeIncrementer
函數(shù)的生命周期要長惩系,我們可以在需要的時機調(diào)用閉包位岔,這個時候這個閉包就是逃逸閉包。
- 使用場景 2
class CXTeacher {
var completionHandle: ((Int) -> Void)?
func makeIncrementer(_ amout: Int, handler: @escaping (Int) -> Void) {
var count = 10
count += amout
DispatchQueue.global().asyncAfter(deadline: .now() + 0.1) {
handler(count)
}
}
func dosomething() {
makeIncrementer(20) {
print($0)
}
}
}
let t = CXTeacher()
t.dosomething()
t.completionHandle?(10)
這里是在原函數(shù)中異步執(zhí)行閉包堡牡,這個時候閉包的生命周期也比原函數(shù)要長抒抬,這時候 handler
也是逃逸閉包。
非逃逸閉包
func testNoEscaping(_ f: () -> Void) {
f()
}
func test() -> Int {
var age = 18
testNoEscaping {
age += 20
}
return 30
}
test()
這個就是一個非逃逸閉包晤柄,當函數(shù)調(diào)用完之后這個閉包也就消失了擦剑。并且非逃逸閉包還有以下優(yōu)點:
- 不會產(chǎn)生循環(huán)引用,函數(shù)作用域內(nèi)釋放
- 編譯器會做更多性能優(yōu)化 (
retain
可免、relsase
) - 上下文的內(nèi)存保存在棧上抓于,不是堆上