我正在尝试将我的项目更新为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))
}
}
Run Code Online (Sandbox Code Playgroud)
有人可以帮我解决这个问题吗?
Ham*_*ish 59
inout专门用于异步任务的参数是滥用inout- 当调用函数时,调用者传递给inout参数的值不会被更改.
这是因为inout它不是pass-by-reference,它只是在函数退出时写回调用者的参数的可变阴影副本 - 并且因为异步函数立即退出,所以不会写回任何更改.
您可以在下面的Swift 2示例中看到这一点,其中一个inout参数可以被转义闭包捕获:
func foo(inout val: String, completion: (String) -> Void) {
dispatch_async(dispatch_get_main_queue()) {
val += "foo"
completion(val)
}
}
Run Code Online (Sandbox Code Playgroud)
var str = "bar"
foo(&str) {
print($0) // barfoo
print(str) // bar
}
print(str) // bar
Run Code Online (Sandbox Code Playgroud)
因为传递的闭包会dispatch_async转义函数的生命周期,所以foo它所做的任何更改val都不会写回调用者的str- 只有通过传递给完成函数才能观察到更改.
在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
}
Run Code Online (Sandbox Code Playgroud)
(编辑:自发布此答案以来,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))
}
}Run Code Online (Sandbox Code Playgroud)
uki*_*kim 10
如果要修改转义闭包中通过引用传递的变量,可以使用 KeyPath。下面是一个例子:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可以在此处查看完整示例。
如果您确定变量始终可用,只需使用真正的指针(与inout实际情况相同)
func foo(val: UnsafeMutablePointer<NSDictionary>, completion: @escaping (NSDictionary) -> Void) {
val.pointee = NSDictionary()
DispatchQueue.main.async {
completion(val.pointee)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
17490 次 |
| 最近记录: |