RxSwift: немного о share(), replay(), shareReplayLatestWhileConnected() и других классных операторах +5


Я уже писал про Publish, Connect и RefCount в RxSwift. Для того, чтобы лучше раскрыть тему, представляю вашему вниманию перевод другой замечательной статьи, про различия между такими операторами, как share(), replay(), replayAll(), shareReplay(), publish() и shareReplayLatestWhileConnected().

Частая ошибка, которую совершают новички, взявшиеся за освоение Rx — это непонимание того, что цепочка операторов на Observable выполняется заново с каждым новым подписчиком:

let results = query.rx.text
    .flatMapLatest { query in
        networkRequestAPI(query)
    }
results.subscribe(...)   // один запрос в сеть
results.subscribe(...)   // другой запрос

Мы имеем несколько подписчиков на один-единственный Observable, но мы не хотим, чтобы его код исполнялся с каждым новым Subscriber'ом. Для этого в RxSwift имеется несколько операторов. Вот резюмирующая табличка, описывающая каждый из них:

image
1 — ретранслирует произведенных до подписки элементов не больше, чем bufferSize.
2 — ретранслирует 1 элемент, произведенный до подписки, до тех пор, пока существует хотя бы один подписчик.


Теперь рассмотрим подробно каждое свойство


Shared subscription. Возвращаемый Observable делит одну основную подписку между всеми подписчиками.

let results = query.rx.text
    .flatMapLatest { query in
        networkRequestAPI(query)
    }
    .любой_приведенный_выше_оператор()

// один запрос в сеть на всех подписчиков
results.subscribe(...)
results.subscribe(...)

Connectable. Возвращаемый Observable не будет производить элементы до тех пор, пока не будет вызван метод connect().

Reference counting. Возвращаемый Observable считает количество сабскрайберов, которые на него подписаны. Как мы помним из первого свойства, на Observable, к которому был применен оператор (его будем называть Source Observable), всегда существует только одна «основная» подписка и она делится данными с остальными подписчиками.

let sourceObservable = query.rx.text
    .flatMapLatest { query in
        networkRequestAPI(query)
    } 
let resultObservable = sourceObservable.share()

resultObservable.subscribe(...)
resultObservable.subscribe(...)
resultObservable.subscribe(...)

// на sourceObservable в данный момент существует только одна подписка
// которая будет шарить свои данные на подписчиков resultsObservable

Когда счетчик подписчиков достигает нуля, происходит очистка ресурсов Source Observable. Обратите внимание: каждый раз, когда счетчик увеличивается с нуля до единицы, на Source Observable будет производиться новая подписка. Но этого может не случиться, если Source Observable до этого завершился или послал ошибку. Это довольно непредсказуемое поведение, поэтому я стараюсь избегать его и при написании кода убеждаюсь, что после того, как количество подписчиков опустилось до нуля, не будет никаких последующих подписок.

Replays events. Оператор ретранслирует элементы из Source Observable на подписчиков, которые подписались уже после того, как Source Observable начал производить элементы. Для replay(bufferSize) и shareReplay(bufferSize) количество этих элементов не больше, чем bufferSize. Для shareReplayLatestWhileConnected() это количество не больше, чем единица. В отличии от shareReplay(bufferSize), shareReplayLatestWhileConnected() очистит буффер, когда количество подписчиков уменьшается до нуля. Поэтому, когда подписывается Subscriber, который увеличит счетчик с нуля до единицы, он не получит никаких элементов.

Так же стоить упомянуть оператор multicast, который может быть полезен, если вы используете Subject'ы. Но, операторов, описанных в этой статье, скорее всего хватит для большинства юзкейсов.

Примечание переводчика.
Для полноты картины советую прочитать:
  1. Cтатью про Publish, Connect и RefCount в RxSwift, там говорилось про первые три свойства, описанных выше.
  2. RxSwift шпаргалка по операторам. Автор проделал большой труд и собрал в одном месте все базовые операторы RxSwift, их описания с картинками и примерами кода. Сам периодически поглядываю в шпаргалку, если надо вспомнить какой-нибудь оператор.
  3. Официальная документация RxSwift, конечно.

Если что-то осталось непонятным, прошу, дайте знать об этом в комментариях.




К сожалению, не доступен сервер mySQL