Происхождение события наблюдения в реактивном подъеме - PullRequest
5 голосов
/ 17 мая 2019

Я прочитал документы несколько раз и нуждаюсь в пояснениях ...

Учитывая приведенный ниже фрагмент:

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 все еще будет живым, пока обрабатывается событие значения?

  • только завершение?Я сомневаюсь, что это так, но, если да, пожалуйста, обратитесь к некоторым документам.Поскольку события завершения не распространяются сразу.

Будет ли использование различных Scheduler s для x и y повлиять на результат?

Наконец, я представляю race?Я сомневаюсь в этом, потому что реактивныйSwift не вводит параллелизм, если это явно не указано разработчиком.

1 Ответ

1 голос
/ 26 июня 2019

Я собрал небольшой пример консольного приложения, чтобы проверить это. Как я уже сказал в своем комментарии, 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.

...