Swift 3.0 错误:转义闭包只能按值显式捕获 inout 参数 [英] Swift 3.0 Error: Escaping closures can only capture inout parameters explicitly by value

查看:14
本文介绍了Swift 3.0 错误:转义闭包只能按值显式捕获 inout 参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to update my project to Swift 3.0 but I have some difficulties. I'm getting next error: "Escaping closures can only capture inout parameters explicitly by value".

问题出在这个函数内部:

The problem is inside this function:

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

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

Can someone help me to fix that?

推荐答案

专门为异步任务使用 inout 参数是对 inout 的滥用——就像调用函数,传入inout 参数的调用者的值不会改变.

Using an inout parameter exclusively for an asynchronous task is an abuse of inout – as when calling the function, the caller's value that is passed into the inout parameter will not be changed.

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

This is because inout isn't a pass-by-reference, it's just a mutable shadow copy of the parameter that's written back to the caller when the function exits – and because an asynchronous function exits immediately, no changes will be written back.

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

You can see this in the following Swift 2 example, where an inout parameter is allowed to be captured by an escaping closure:

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 - 只有通过传递到完成函数才能观察到更改.

Because the closure that is passed to dispatch_async escapes the lifetime of the function foo, any changes it makes to val aren't written back to the caller's str – the change is only observable from being passed into the completion function.

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

In Swift 3, inout parameters are no longer allowed to be captured by @escaping closures, which eliminates the confusion of expecting a pass-by-reference. Instead you have to capture the parameter by copying it, by adding it to the closure's capture list:

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 可以看到发出.但是您不能这样对待它们,因为无法保证无论如何调用者的值在函数调用后仍然有效.)

( Since posting this answer, inout parameters can now be compiled as a pass-by-reference, which can be seen by looking at the SIL or IR emitted. However you are unable to treat them as such due to the fact that there's no guarantee whatsoever that the caller's value will remain valid after the function call.)

但是,就您而言,根本不需要 inout.您只需要将请求中的结果数组附加到您传递给每个请求的当前结果数组中.

However, in your case there's simply no need for an inout. You just need to append the resultant array from your request to the current array of results that you pass to each request.

例如:

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 参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆