#swift #combine
#swift #объединить
Вопрос:
Следующий код выдает результат, который я ожидаю:
import UIKit
import Combine
let myURL = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")
// MARK: - Region
struct Region: Codable {
let country: String
let subregions: [String]
}
let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: myURL!)
.map { $0.data }
.print("Hello Data")
.decode(type: Region.self, decoder: JSONDecoder())
let cancellableSink = remoteDataPublisher
.sink(receiveCompletion: { completion in
print(".sink() received the completion", String(describing: completion))
switch completion {
case .finished:
break
case .failure(let anError):
print("received error: ", anError)
}
}, receiveValue: { someValue in
print(".sink() received (someValue)")
})
// =====================================================================================================
print("The End.")
Вот результат, показанный в консоли.
Как вы можете видеть, «отмены» нет:
Однако, когда я обертываю код в функцию (), ‘combine’ ОТМЕНЯЕТ вывод.
Следующий код в getRegionList() не выдает результат. Вместо этого он получает «отмену», как показано в консоли, следуя приведенному ниже коду:
import UIKit
import Combine
var regionList = [String]()
let simplePublisher = PassthroughSubject<String, Never>()
func getRegionList() {
let myURL = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")
// MARK: - Region
struct Region: Codable {
let country: String
let subregions: [String]
}
let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: myURL!)
.map { $0.data }
.print("Hello Data")
.decode(type: Region.self, decoder: JSONDecoder())
let cancellableSink = remoteDataPublisher
.sink(receiveCompletion: { completion in
print(".sink() received the completion", String(describing: completion))
switch completion {
case .finished:
break
case .failure(let anError):
print("received error: ", anError)
}
}, receiveValue: { someValue in
print(".sink() received (someValue)")
})
}
getRegionList()
print("The End.")
Функция отменяется, как показано на консоли:
Почему?
Единственное отличие кода в том, что он заключен в простую функцию.
Я подозреваю, что какой-то «срок службы», должно быть, истек.
Решение: должен ли я сделать concellableSink глобальным?
Каково правильное решение / синтаксис?
Ответ №1:
Вам нужно сохранить ссылку на AnyCancellable
, в противном случае, когда она освобождается, она отменяет подписку, что и происходит, когда ваша функция возвращается.
Обычно это обрабатывается путем сохранения ссылки в AnyCancellable
свойстве (или Set<AnyCancellable>
) экземпляра:
class Foo {
var cancellables: Set<AnyCancellable> = []
//...
func doSomething() {
somePublisher
.sink(...)
.store(in: amp;cancellables)
}
}
В качестве альтернативы, sink
закрытие может сохранять ссылку на свой собственный AnyCancellable и освобождать его при получении значения, но, конечно, это может быть утечкой памяти, если издатель никогда ничего не публикует (хотя вы могли бы смягчить это с .timeout
помощью оператора):
func doSomething() {
let cancellable: AnyCancellable? = nil
cancellable = somePublisher
.sink(receiveCompletion: { completion in
// ...
cancellable = nil
}, receiveValue: { value in
//...
})
}