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

我试图更新我的项目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参数专门用于asynchronous任务是inout的滥用,因为在调用函数时,传入inout参数的调用者的值不会被更改。

这是因为inout不是通过引用传递的,它只是当函数退出时写回给调用者的参数的可变阴影副本 – 而且因为asynchronous函数立即退出,所以不会写回任何更改。

你可以在下面的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 – 只有在传递到completion函数中才能观察到变化。

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