我正在尝试将我的项目更新到 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/