当前位置: 首页 > 面试题库 >

Swift 3.0错误:转义的闭包只能按值显式捕获inout参数

左仰岳
2023-03-14
问题内容

我正在尝试将项目更新为Swift 3.0,但遇到了一些困难。我收到下一个错误:“转义的闭包只能按值显式捕获inout参数”。

问题出在此函数内部:

fileprivate func collectAllAvailable(_ storage: inout [T], nextUrl: String, completion: @escaping CollectAllAvailableCompletion) {
    if let client = self.client {
        let _ : T? = client.collectionItems(nextUrl) {

            (resultCollection, error) -> Void in

            guard error == nil else {
                completion(nil, error)
                return
            }

            guard let resultCollection = resultCollection, let results = resultCollection.results else {
                completion(nil, NSError.unhandledError(ResultCollection.self))
                return
            }

            storage += results // Error: Escaping closures can only capture inout parameters explicitly by value

            if let nextUrlItr = resultCollection.links?.url(self.nextResourse) {

                self.collectAllAvailable(&storage, nextUrl: nextUrlItr, completion: completion) 
                // Error: Escaping closures can only capture inout parameters explicitly by value

            } else {
                completion(storage, nil) 
                // Error: Escaping closures can only capture inout parameters explicitly by value
            }
        }
    } else {
        completion(nil, NSError.unhandledError(ResultCollection.self))
    }
}

有人可以帮我解决这个问题吗?


问题答案:

inout仅将参数用于异步任务是一种滥用inout–就像在调用函数时一样,传递给inout参数的调用者的值不会更改。

这是因为inout它不是传递引用,它只是参数的可变影子副本,该副本在函数退出时写回到调用方–并且由于异步函数立即退出,因此不会写回任何更改。

您可以在下面的Swift 2示例中看到这一点,其中inout转义的闭包允许捕获参数:

func foo(inout val: String, completion: (String) -> Void) {
    dispatch_async(dispatch_get_main_queue()) {
        val += "foo"
        completion(val)
    }
}
var str = "bar"
foo(&str) {
    print($0) // barfoo
    print(str) // bar
}
print(str) // bar

由于传递给该闭包dispatch_async的函数将逃逸该函数的生命周期,因此foo对它所做的任何更改val都不会写回到调用方的str-只能将更改传递给完成函数才能观察到。

在Swift 3中,闭包inout不再允许捕获参数@escaping,这消除了期望传递引用的困惑。相反,您必须通过 复制 参数来捕获参数,方法是

其添加到闭包的捕获列表中:

func foo(val: inout String, completion: @escaping (String) -> Void) {
    DispatchQueue.main.async {[val] in // copies val
        var val = val // mutable copy of val
        val += "foo"
        completion(val)
    }
    // mutate val here, otherwise there's no point in it being inout
}

编辑:
自发布此答案以来,inout现在可以将参数编译为通过引用,可以通过查看所发出的SIL或IR来查看。但是由于没有这样的事实,您无法将其视为此类引用保证
任何 调用者的价值将在函数调用后仍然有效。)

但是,根据您的情况,根本不需要inout。您只需要将请求中的结果数组附加到传递给每个请求的当前结果数组中即可。

例如:

fileprivate func collectAllAvailable(_ storage: [T], nextUrl: String, completion: @escaping CollectAllAvailableCompletion) {
    if let client = self.client {
        let _ : T? = client.collectionItems(nextUrl) { (resultCollection, error) -> Void in

            guard error == nil else {
                completion(nil, error)
                return
            }

            guard let resultCollection = resultCollection, let results = resultCollection.results else {
                completion(nil, NSError.unhandledError(ResultCollection.self))
                return
            }

            **let storage = storage + results // copy storage, with results appended onto it.**

            if let nextUrlItr = resultCollection.links?.url(self.nextResourse) {
                self.collectAllAvailable(storage, nextUrl: nextUrlItr, completion: completion) 
            } else {
                completion(storage, nil) 
            }
        }
    } else {
        completion(nil, NSError.unhandledError(ResultCollection.self))
    }
}


 类似资料:
  • 我正在尝试将我的项目更新为Swift 3.0,但我遇到了一些困难。我得到了下一个错误:“转义闭包只能通过值显式捕获inout参数”。 问题出在以下函数内部: 有人可以帮我解决这个问题吗?

  • 问题内容: 我正在使用Firebase观察事件,然后在完成处理程序中设置图像 但是我收到此错误 闭包不能隐式捕获变异的自身参数 我不确定这个错误是什么,寻找解决方案并没有帮助 问题答案: 短版 拥有您的调用的类型很可能是值类型(a ?),在这种情况下,变异上下文可能不会在闭包中显式捕获。 一种简单的解决方案是将您的拥有类型更新为一次引用()。 较长的版本 Firebase的方法声明如下 所述封闭件

  • 问题内容: 测试代码如下: 我认为输出将是:java,python,erlang,cpp,go;但是输出是:go go go go go; 怎么了 问题答案: 就是这样写函数,把动词变成函数

  • 闭包本身是相当灵活的,可以实现所需功能来让闭包运行而不用类型标注(原文:Closures are inherently flexible and will do what the functionality requires to make the closure work without annotation)。这允许变量捕获灵活地适应使用 情况,有时是移动(moving)有时是借用(borro

  • 我有一个协议: 通过一个示例实现: 上面的代码在 Swift3 (Xcode8-beta5) 中编译和工作,但不再与 beta 6 一起使用。你能给我指出根本原因吗?

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