Я читал документы несколько раз и нуждаюсь в разъяснении...
Учитывая фрагмент ниже:
let signal: Signal<Value,Error>
//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
}
Если signal
срабатывает и уведомляет своих наблюдателей о value event
:
1) Наблюдатель y
будет уведомлен раньше x
(предположение, потому что он впервые наблюдается раньше в очереди)
2) Поскольку y
завершится после обработки значения, self
после этого следует освободить
Вопрос:
Какие события x
получат (по порядку):
ценность и завершение? Гарантируется ли, что self все еще будет жив, пока обрабатывается событие value?
только завершение? Я сомневаюсь, что это так, но если это так, пожалуйста, сошлитесь на какой-нибудь документ. Поскольку события завершения не распространяются немедленно.
Повлияет ли использование разных Scheduler
для x
и y
на результат?
Наконец, я представляю race
? Я сомневаюсь в этом, потому что reactiveSwift не вводит параллелизм, если это явно не указано разработчиком.
Я собрал небольшой образец консольного приложения, чтобы проверить это. Как я сказал в своем комментарии, take(first: 1)
доставляет событие завершения синхронно сразу после передачи события 1 значения, что означает, что ссылка y
на 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
.
Теперь это на самом деле приведет к гонке, потому что событие завершения take
будет доставлено в любой планировщик, который вы настроили для этого наблюдателя, и доставка этого события вызовет deinit
из self
.
Вы проверяли это? Насколько я понимаю,
take(first: 1)
доставляет событие завершения синхронно сразу после передачи события 1 значения, что означает, что ссылкаy
s наself
исчезнет до того, как какие-либо значения будут доставлены вx
. Предполагая, что это единственная сильная ссылка наself
, я не верю, чтоx
получит какие-либо значения. Тем не менее, эксперимент всегда лучший способ подтвердить.