Advertisement

Swift中@escaping的理解与使用

阅读量:

当我们在一个方法中传递一个闭包作为参数时,通常会使用这个@escaping关键字。通常会将闭包作为参数传进来吗?很多时候,在方法中有这样的异步操作:method在执行完返回结果之前就会调用内部的函数或执行其他任务。很多时候,在方法中有这样的异步操作:method会在执行完返回结果之前就会调用内部的函数或执行其他任务以保持状态或等待响应。很多时候,在方法中有这样的异步操作:method会在完成所有内部操作后才会通过return返回结果以供外部使用。

@escaping修饰的闭包将跳出其所处的作用域并在外部继续存活。换句话说,在方法结束后该闭包装入了外部作用域从而依然存活。未被@escaping修饰的闭包则在方法退出时自然终止。

首先先看一个没有@escaping关键字修饰的闭包。

复制代码
    func executeSomeRequest(_ closure: () -> Void) {
    closure()
    }

此方法得以成功编译。其中,在executeSomeRequest方法内部,直接调用了closure()闭包,并随后该方法完成运行。该闭包在其所关联的方法生命周期内完成运行。

那么如果闭包没有在方法的生命周期内执行完毕,比如延迟执行闭包。

复制代码
    func executeSomeRequest(_ closure: () -> Void) {
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
        closure()
    }
    }

结果得到下面的报错:

在这里插入图片描述

报错是因为编译器认为传入的闭包将越出其所在的方法的作用域。为了使这样的闭包能够越出其所在的方法的作用域,在声明该关闭时必须使用@escaping关键字进行修饰。这种类型的关闭在技术领域常被称为"逃逸关闭"(escaped closures)。这通常会告知调用者这些关闭的行为可能会越出作用域范围,请注意处理。

修改后:

复制代码
    func executeSomeRequest(_ closure: @escaping () -> Void) {
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
        closure()
    }
    }

还有一种比较常用场景就是在请求数据的时候,比如下面这种:

复制代码
    func requestData(_ completion: @escaping (Data?, NetworkError?) -> Void) {
    URLSession.shared.dataTask(with: URLRequest(url: URL(string: "https://xxxx")!)) { data, response, error in
        if let data {
            completion(data, nil)
        } else {
            completion(nil, .feedError("something wrong"))
        }
    }
    }

在RequestData方法中向外部发送了一个请求之后,在数据返回时调用传入的Completion闭包,则该闭包必定脱离了该函数体的控制范围;因此,在其内部必须使用@escaping进行修饰。

除了前面提到的情况之外,在某个特定时机调用这个方法时会遇到问题

复制代码
    func requestData2(_ completion: @escaping (Data?, NetworkError?) -> Void) {
    self.completion = completion
    }

总而言之表明@escaping告诉方法调用者这一信息,该闭包的存在时长大于其被调用的方法,在内存中某个位置存放着这一特性,需要用适当的方式防止循环引用以及内存泄漏问题.

分析使用逃逸闭包的优缺点:
优点:该方法在处理异步操作和回调函数方面表现出色,在处理大量并行操作时展现出卓越的效果。
缺点:这些闭包能够捕获并维持引用;当未妥善管理时可能导致内存泄漏问题。

最后一条经验分享希望能帮助到需要的朋友。看到这些内容对您有所帮助,请别忘了点赞并收藏哦!未来我会持续改进和完善自己,带来更多优质的体验。

全部评论 (0)

还没有任何评论哟~