Я собрал небольшой пример консольного приложения, чтобы проверить это. Как я уже сказал в своем комментарии, take(first: 1)
доставляет событие завершения синхронно сразу после передачи события 1 значения, что означает, что ссылка y
s на self
исчезнет до того, как какие-либо значения будут доставлены в x
. Если предположить, что это единственная надежная ссылка на self
, x
не получит никаких значений.
import Foundation
import ReactiveSwift
import ReactiveCocoa
class MyClass {
init(signal: Signal<String, Never>) {
//call this observer y
signal.take(first: 1).observeValues{ (value) in
//intended strong capture on self. this is the only one that retains self so if this observer is triggered and completes, self should dealloc
self.doSomethingElse(value) //trivial call, no async or thread hopping
}
//call this observer x
signal.take(duringLifetimeOf: self).observeValues{ [unowned self] (value) in //is this safe or better to use weak and guard against it?
self.doSomeProcess(value) //trivial call, no async or thread hopping
}
}
func doSomethingElse(_ value: String) {
print("Something Else: \(value)")
}
func doSomeProcess(_ value: String) {
print("Some Process: \(value)")
}
}
let (signal, input) = Signal<String, Never>.pipe()
_ = MyClass(signal: signal)
input.send(value: "1")
input.send(value: "2")
Конечно, doSomeProcess
никогда не вызывается:
Something Else: 1
Program ended with exit code: 0
Главное, что нужно помнить о ReactiveSwift
, это то, что все происходит синхронно, если вы явно не укажете иное с помощью определенного набора операторов или своего собственного кода. Таким образом, оператор take
не отправляет событие с одним значением, а затем каким-то образом «планирует» доставку события завершения на более позднее время. Доставка как значения, так и события завершения происходит во время передачи сигнала значения восходящего потока события значения, и освобождение наблюдателя и его ссылок происходит до того, как signal
завершит доставку своего первого события.
Когда вы говорите «события завершения не распространяются немедленно», я предполагаю, что вы говорите о той части APIContracts
файла , в которой говорится о том, как сбои и прерывания распространяются немедленно. Это просто означает, что многие операторы немедленно передают эти события, даже если они являются асинхронными или смещающими время операторами.
Оператор take
не является оператором смещения во времени или асинхронным. И в этом случае оператор не передает событие завершения из восходящего сигнала; скорее это генерирует само событие завершения, а делает это синхронно сразу после того, как распространяет событие значения .
Представляю ли я гонку?
Вы правы, что ReactiveSwift
не вводит асинхронность или параллелизм сам по себе, поэтому здесь нет "расы" в традиционном смысле. Однако я считаю, что контракт API на Signal
не гарантирует, что события доставляются наблюдателям в порядке, в котором они начали наблюдать. Таким образом, поведение этого кода технически не определено и может измениться в будущих версиях ReactiveSwift
.
Повлияет ли использование разных планировщиков на x и y на результат?
Теперь это фактически приведет к гонке, потому что событие завершения take
будет доставлено на любой планировщик, который вы настроили для этого наблюдателя, и доставка этого события вызовет deinit
из self
.