публикация значения NSObject по KeyPath с использованием AsyncStream - проблема эталонного цикла
Я использую параллелизм функций Swift. Я создал вспомогательную функцию, которая возвращает AsyncStream со значениями, опубликованными реализациями NSOBject. Вид кода ниже.
func asyncStreamFor<Root: NSObject, Value> (_ root: Root, keyPath: KeyPath<Root, Value>) -> AsyncStream<Value> {
AsyncStream { continuation in
let cancellable = root.publisher(for: keyPath, options: [.initial, .new])
.sink {
continuation.yield($0)
}
continuation.onTermination = { @Sendable _ in
cancellable.cancel()
}
}
}
Я пытаюсь использовать его (и ранее использовал его непосредственно с помощью издателя) для таких целей, как наблюдение за свойствами AVPlayer (скорость, статус). Схема использования ниже:
class Player {
let avPlayer = AVPlayer()
var cancellable = Set<AnyCancellable>()
init() {
avPlayer.publisher(for: \.status)
.sink {
print($0)
}.store(in: &cancellable)
}
}
Выпуск экземпляра Player работает правильно (проблем с циклом нет).
Для AsyncStream я попытался упростить и использовать схему, как показано ниже:
class Player {
let avPlayer = AVPlayer()
init() {
Task {
for await status in asyncStreamFor(avPlayer, keyPath: \.status) {
print(status)
}
}
}
}
Здесь, кажется, есть эталонный цикл: экземпляр AsyncStream содержит ссылку на отмену (в закрытии onTermination), которая, в свою очередь, содержит ссылку на avPlayer. Экземпляр игрока не деинициализируется при удалении последней ссылки.
Любая идея, как решить проблему без явной отмены задачи перед удалением ссылки на проигрыватель?