Ответ 1
Использование параметра 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
, что устраняет путаницу в ожидании передачи по ссылке. Вместо этого вы должны зафиксировать параметр, скопировав его, добавив его в список :
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 no point in it being inout
}
( Изменить:. После отправки этого ответа параметры inout
теперь могут быть скомпилированы как перекрестная ссылка, что можно увидеть, посмотрев на SIL или ИК излучаемый. неспособность рассматривать их как таковые из-за того, что нет никакой гарантии, что значение вызывающего абонента останется в силе после вызова функции.)
Однако в вашем случае просто нет необходимости в 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))
}
}