jjzjj

ios - swift 3.0 错误 : Escaping closures can only capture inout parameters explicitly by value

coder 2023-07-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 闭包捕获,这消除了期望传递引用的混淆。相反,您必须通过复制 来捕获参数,将其添加到闭包的 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 看出emitted。但是你不能这样对待它们,因为不能保证无论如何调用者的值在函数调用后仍然有效。)


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

            <b>let storage = storage + results // copy storage, with results appended onto it.</b>

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

关于ios - swift 3.0 错误 : Escaping closures can only capture inout parameters explicitly by value,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39569114/

有关ios - swift 3.0 错误 : Escaping closures can only capture inout parameters explicitly by value的更多相关文章

随机推荐