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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39569114/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 10:15:00  来源:igfitidea点击:

Swift 3.0 Error: Escaping closures can only capture inout parameters explicitly by value

iosswiftswift3

提问by walkline

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".

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

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?

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

回答by Hamish

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

inout参数专门用于异步任务是一种滥用inout- 因为在调用函数时,传递给inout参数的调用者的值不会改变。

This is because inoutisn'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.

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

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

您可以在以下 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(
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
}
) // barfoo print(str) // bar } print(str) // bar

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

因为传递给的闭包dispatch_async逃过了函数的生命周期,所以foo它所做的任何更改val都不会写回调用者的str——更改只能从传递到完成函数中观察到。

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

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

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

(Edit:Since posting this answer, inoutparameters 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 whatsoeverthat the caller's value will remain valid after the function call.)

编辑:自从发布此答案后,inout现在可以将参数编译为传递引用,这可以通过查看发出的 SIL 或 IR 来看到。但是,由于没有保证任何调用者的价值将在函数调用后仍然有效。)



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.

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

For example:

例如:

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

回答by ukim

If you want to modify a variable passed by reference in an escaping closure, you can use KeyPath. Here is an example:

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

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

You can see the full example here.

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

回答by John Smith

If you are sure that your variable will be available the whole time just use a true Pointer (same what inoutactually does)

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

##代码##