美文网首页Swift
Swift 闭包(二)

Swift 闭包(二)

作者: 晨曦的简书 | 来源:发表于2022-01-26 16:12 被阅读0次

    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 语句出现在同一作用域中,则它们出现的顺序与它们执行的顺序相反,也就是先出现的后执行。

    • 案例 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()
    }
    

    在我们使用指针的时候,allocatedeallocateinitializedeinitialize 都是成对出现的,这时候我们就可以将 deallocatedeinitialize 放到 defer {} 中。

    • 案例 4
    func netRquest(completion: () -> Void) {
        defer {
            self.isLoading = false
            completion()
        }
        guard error == nil else { return }
    }
    

    比如我们在进行网络请求的时候,可能有不同的分支进行回调函数的执行,这个时候可以使用 defer {} 来管理 completion 回调,前提是这里是非异步的,defer {} 就是方便我们来管理代码块。

    defer 使用注意

    使用 defer 的时候要避免像这样书写,编译器也会提示 defer 可能不会立即执行。

    这里需要注意的是 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()
    

    这个就是一个非逃逸闭包,当函数调用完之后这个闭包也就消失了。并且非逃逸闭包还有以下优点:

    • 不会产生循环引用,函数作用域内释放
    • 编译器会做更多性能优化 (retainrelsase)
    • 上下文的内存保存在栈上,不是堆上

    相关文章

      网友评论

        本文标题:Swift 闭包(二)

        本文链接:https://www.haomeiwen.com/subject/ukpwhrtx.html