Swift 闭包(二)
OC Block 和 Swift 闭包相互调用
我们在 OC
中定义的 Block
,在 Swift
中是如何调用的呢?我们来看一下
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 关键字介绍
@convention
:用于修饰函数类型
- 修饰
Swift
中的函数类型(调用C
函数的时候) - 调用
OC
方法时,修饰Swift
函数类型
defer 的用法
定义:defer {}
里的代码会在当前代码块返回的时候执行,无论当前代码块是从哪个分支 return
的,即使程序抛出错误,也会执行。
- 案例 1
如果多个 defer
语句出现在同一作用域中,则它们出现的顺序与它们执行的顺序相反,也就是先出现的后执行。
![](https://img.haomeiwen.com/i2936157/8c4c6972716cafc6.png)
- 案例 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开发语言", toFileAt: url)
try append(string: "Swift", toFileAt: url)
这里有时候如果当前方法中多次出现 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
都是成对出现的,这时候我们就可以将 deallocate
跟 deinitialize
放到 defer {}
中。
- 案例 4
func netRquest(completion: () -> Void) {
defer {
self.isLoading = false
completion()
}
guard error == nil else { return }
}
比如我们在进行网络请求的时候,可能有不同的分支进行回调函数的执行,这个时候可以使用 defer {}
来管理 completion
回调,前提是这里是非异步的,defer {}
就是方便我们来管理代码块。
defer 使用注意
![](https://img.haomeiwen.com/i2936157/5177c577f26a7c17.png)
使用 defer
的时候要避免像这样书写,编译器也会提示 defer
可能不会立即执行。
![](https://img.haomeiwen.com/i2936157/54bfe4c057a9ae90.png)
这里需要注意的是 temp
的值还是 1,因为 defer {}
是在返回之后执行。其实这里 defer
这样使用并没有意义,我们要避免这样的写法,defer
我们要用来管理一些统一资源, 优化冗余代码, 使代码更加简洁直观。
逃逸闭包
逃逸闭包:当闭包作为一个实际参数传递给一个函数的时候,并且是在函数返回之后调用,我们就说这个闭包逃逸了。当我们声明一个接受闭包作为形式参数的函数时,你可以在形式参数前加上 @escaping
来明确闭包是允许逃逸的。
- 作为函数的参数传递
- 当前闭包在函数内部异步执行或者被存储
- 函数结束,闭包被调用,生命周期结束
注意:可选类型默认是逃逸闭包!
- 使用场景 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
函数中将闭包参数 handler
赋值给 completionHandle
,这时候闭包的生命周期比 makeIncrementer
函数的生命周期要长,我们可以在需要的时机调用闭包,这个时候这个闭包就是逃逸闭包。
- 使用场景 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)
这里是在原函数中异步执行闭包,这个时候闭包的生命周期也比原函数要长,这时候 handler
也是逃逸闭包。
非逃逸闭包
func testNoEscaping(_ f: () -> Void) {
f()
}
func test() -> Int {
var age = 18
testNoEscaping {
age += 20
}
return 30
}
test()
这个就是一个非逃逸闭包,当函数调用完之后这个闭包也就消失了。并且非逃逸闭包还有以下优点:
- 不会产生循环引用,函数作用域内释放
- 编译器会做更多性能优化 (
retain
、relsase
) - 上下文的内存保存在栈上,不是堆上