当前位置: 首页 > 知识库问答 >
问题:

非转义参数的闭包使用可能会允许它转义

公西翼
2023-03-14

我有一个协议:

enum DataFetchResult {
    case success(data: Data)
    case failure
}

protocol DataServiceType {
    func fetchData(location: String, completion: (DataFetchResult) -> (Void))
    func cachedData(location: String) -> Data?
}

通过一个示例实现:

    /// An implementation of DataServiceType protocol returning predefined results using arbitrary queue for asynchronyous mechanisms.
    /// Dedicated to be used in various tests (Unit Tests).
    class DataMockService: DataServiceType {

        var result      : DataFetchResult
        var async       : Bool = true
        var queue       : DispatchQueue = DispatchQueue.global(qos: .background)
        var cachedData  : Data? = nil

        init(result : DataFetchResult) {
            self.result = result
        }

        func cachedData(location: String) -> Data? {
            switch self.result {
            case .success(let data):
                return data
            default:
                return nil
            }
        }

        func fetchData(location: String, completion: (DataFetchResult) -> (Void)) {

            // Returning result on arbitrary queue should be tested,
            // so we can check if client can work with any (even worse) implementation:

            if async == true {
                queue.async { [weak self ] in
                    guard let weakSelf = self else { return }

                    // This line produces compiler error: 
                    // "Closure use of non-escaping parameter 'completion' may allow it to escape"
                    completion(weakSelf.result)
                }
            } else {
               completion(self.result)
            }
        }
    }

上面的代码在 Swift3 (Xcode8-beta5) 中编译和工作,但不再与 beta 6 一起使用。你能给我指出根本原因吗?

共有2个答案

云韬
2023-03-14

由于@noescape是默认值,因此有2个选项可以修复错误:

1) 正如@Hamish在回答中指出的那样,如果您确实关心结果并确实希望它逃逸,只需将完成标记为@escaping即可(这可能是@Lukasz的问题,以单元测试为例,以及异步完成的可能性)

func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void)

2) 通过使完成成为可选,保留默认@noescape行为,在您不关心结果的情况下完全放弃结果。例如,当用户已经“走开”并且调用视图控制器不必仅仅因为有一些粗心的网络调用而挂起在内存中时。就像我来这里寻找答案时的情况一样,示例代码与我不是很相关,所以标记@noescape不是最好的选择,尽管乍一看它听起来是唯一一个。

func fetchData(location: String, completion: ((DataFetchResult) -> Void)?) {
   ...
   completion?(self.result)
}
龚玄天
2023-03-14

这是由于函数类型参数的默认行为发生了变化。在Swift 3(特别是Xcode 8 beta 6附带的构建)之前,它们将默认为转义-您必须将它们标记为@noscape以防止它们被存储或捕获,这保证它们不会超过函数调用的持续时间。

但是,现在函数类型参数的默认值是@noescape。如果要存储或捕获此类函数,现在需要将它们标记为转义:

protocol DataServiceType {
  func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void)
  func cachedData(location: String) -> Data?
}

这是一个很好的例子

func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void) {
  // ...
}

有关此更改的更多信息,请参阅Swift Evolution提案。

 类似资料:
  • 问题内容: 我有一个协议: 通过示例实现: 上面的代码已在Swift3(Xcode8-beta5)中编译并工作,但不再与beta 6一起工作。您能指出我的根本原因吗? 问题答案: 这是由于功能类型参数的默认行为发生了变化。在Swift 3(特别是Xcode 8 beta 6附带的内部版本)之前,它们将默认转义- 您必须对其进行标记,以防止它们被存储或捕获,从而确保它们不会超过使用寿命函数调用。 但

  • 我知道Swift 3中的更改,其中@nonevinging是闭包的默认行为。 我已经成功地更改了有关更改的大部分代码,但我的代码中有一部分无法摆脱闭包使用非转义参数可能允许它转义编译错误。 我尝试过在updateHandler参数和UpdatedInProgressHandler typealias中添加@逃逸,但这似乎还不够。 有人能帮我找出问题的原因吗? 定义typealiases和函数的代码

  • 问题内容: 鉴于: 有什么方法可以使参数(和)的类型也保持不变? 更改类型会出现以下错误: @escaping属性仅适用于函数类型 删除该属性后,代码将编译并运行,但由于闭包使函数的作用范围变大,因此似乎并不正确。 问题答案: 有一个SR-2552报告无法识别功能类型别名。这就是错误的原因。您可以通过扩展函数签名中的函数类型来解决: 编辑1 : 我实际上是在xcode 8 beta版本下,但尚未解

  • 鉴于: 有什么方法可以使类型的参数(和)并且还保留? 更改类型会产生以下错误: @escaping属性仅适用于函数类型 删除属性,代码将编译并运行,但似乎不正确,因为闭包正在转义函数的范围。

  • 问题内容: 希望能帮助您理解“ Java并发实践”中的以下内容: 从构造函数中调用可重写的实例方法(既不是私有方法也不是final方法)也可以使this引用转义。 这里的“转义”是否仅表示在实例完全构建之前,我们可能正在调用实例方法? 我看不到“ this”以任何其他方式逃避了实例的范围。 ‘最终’如何防止这种情况的发生?我缺少实例创建中的’最终’某些方面吗? 问题答案: 这意味着在类之外调用代码

  • 问题内容: 编译器错误Closure use of non-escaping parameter ‘completion’ may allow it to escape,这是有道理的,因为它将在函数 返回之后被调用。 但是,如果我将闭包设为可选,则不会出现编译器错误,那是为什么呢? 函数返回后仍可以调用闭包。 问题答案: Clarification: 为了理解这种情况,实现以下代码将很有用: 乍一