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

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))
    }
}

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

共有3个答案

晏和风
2023-03-14

如果您确定您的变量将始终可用,只需使用真正的指针(与inout实际执行的操作相同)

func foo(val: UnsafeMutablePointer<NSDictionary>, completion: @escaping (NSDictionary) -> Void) {
    val.pointee = NSDictionary()
    DispatchQueue.main.async {
        completion(val.pointee)
    }
}
姬选
2023-03-14

如果要修改在转义闭包中通过引用传递的变量,可以使用KeyPath。下面是一个例子:

class MyClass {
    var num = 1
    func asyncIncrement(_ keyPath: WritableKeyPath<MyClass, Int>) {
        DispatchQueue.main.async {
            // Use weak to avoid retain cycle
            [weak self] in
            self?[keyPath: keyPath] += 1
        }
    }
}

您可以在此处查看完整示例。

嵇弘新
2023-03-14

仅对异步任务使用 inout 参数是对 inout 的滥用 – 因为在调用函数时,传递到 inout 参数中的调用方值不会更改。

这是因为<code>inout</code>不是一个传递引用,它只是函数退出时写回调用方的参数的可变卷影副本,并且因为异步函数立即退出,所以不会写回任何更改。

您可以在以下 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

因为传递给< code>dispatch_async的闭包会对函数< code>foo的生存期进行转义,所以它对< code>val所做的任何更改都不会被写回到调用者的< code>str中——这种更改只有在传递到完成函数中时才能被观察到。

在Swift 3中,inout参数不再允许由@逃逸闭包捕获,这消除了期望通过引用传递的混乱。相反,您必须通过复制它来捕获参数,将其添加到闭包的捕获列表中:

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
}

(编辑:自从发布此答案后,< code>inout参数现在可以编译为按引用传递,这可以通过查看发射的SIL或IR来查看。但是你不能这样对待它们,因为无论如何也不能保证调用者的值在函数调用后仍然有效。)

但是,在您的情况下,根本不需要< code>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

  • 问题内容: 我们有一个较旧的ASP.NET WebForms应用程序,该应用程序通过在客户端使用jQuery 调用来执行AJAX请求,并在用属性修饰的代码背后的页面中调用静态方法。 如果WebMethod中发生未处理的异常,它将不会触发该事件,因此不会被我们的错误记录器(ELMAH)接收。这是众所周知的,不是问题- 我们将所有WebMethod代码包装在try-catch块中,但例外情况已手动记录

  • 来自声明/功能/9.3.4.6/6.2(我很抱歉如何引用标准中的具体句子): 显式对象参数声明是带有this说明符的参数声明。显式对象参数声明只能作为参数声明列表的第一个参数声明出现:(6.1)声明成员函数的成员声明符([class.mem])或(6.2)lambda声明符([expr.prim.lambda])。 如果lambda表达式允许此作为显式对象参数,那么当我们同时捕获变量时会发生什么?