RxSwift — Определение того, было ли удалено наблюдаемое

#ios #swift #rx-swift

Вопрос:

Я пытаюсь выяснить Publisher , какие источники поступают Observables его клиентам Consumer , чтобы определить, когда один из его потребителей избавился от них Observable .

Досадно. мой код работал нормально, пока я не удалил RxSwift .debug из Consumer кода.

Есть ли какой-то альтернативный способ, которым я мог бы заставить это работать?

 private class Subscriber {  var ids: [Int]  // This property exists so I can watch whether the observable has   // gone nil (which I though would happen when its been disposed, but it   // seems to happen immediately)  weak var observable: Observablelt;[Updates]gt;? }  
 class Publisher {  private let relay: BehaviorRelaylt;[Int: Updates]gt;   private var subscribers: [Subscriber] = []   func updatesStream(for ids: [Int]) -gt; Observablelt;[Updates]gt; {  let observable = relay  .map { map in  return map  .filter { ids.contains($0.key) }  .map { $0.value }  }  .filter { !$0.isEmpty }  .asObservable()   let subscriber = Subscriber(ids: ids, observable: observable)  subscribers.append(subscriber)  return observable  }   private func repeatTimer() {  let updates: [Updates] = ....   // I need to be able to determine at this point whether the subscriber's   // observable has been disposed of, so I can remove it from the list of  // subscribers. However `subscriber.observable` is always nil here.  // PS: I am happy for this to happen before the `repeatTimer` func fires  subscribers.remove(where: { subscriber in  return subscriber.observable == nil  })   relay.accept(updates)  } }  
  class Client {   private var disposeBag: DisposeBag?  private let publisher = Publisher()   func startWatching() {  let disposeBag = DisposeBag()  self.disposeBag = disposeBag   publisher  // with the `.debug` below things work OK, without it the   ///`Publisher.Subscriber.observable` immediately becomes nil  //.debug("Consumer")   .updatesStream(for: [1, 2, 3])  .subscribe(onNext: { values in  print(values)  })  .disposed(by: disposeBag)  }   func stopWatching() {  disposeBag = nil  } }  

Комментарии:

1. Вы должны научиться понимать функциональную природу FRP. Наблюдаемые типы всегда должны объявляться с использованием let никогда var и особенно не слабых переменных. Я не уверен в контексте, в котором вы вызываете repeatTimer , но функция кажется противоречащей тому, как следует использовать наблюдаемые объекты… Возможно, если вы дадите больше контекста, один из нас сможет помочь вам построить абстракцию, которая лучше соответствует парадигме.

2. Спасибо за ответ. repeatTimer на самом деле в производственном коде этого нет, я просто добавил это, чтобы продемонстрировать, что внешние обновления могут появляться в произвольное время и из неизвестных источников. Хотя я уважаю ваше предложение об использовании FRP, к сожалению, это не код, который я могу изменить в данном случае.

3. Проблема здесь в том, что наблюдаемые объекты никогда не удаляются. Подписки удаляются, и на любой конкретной наблюдаемой странице может быть много подписок. Поэтому нет никакого смысла даже спрашивать, было ли «удалено наблюдаемое». Опять же, нам нужно больше контекста, чтобы найти решение.

Ответ №1:

Я думаю, что это очень плохая идея, но она решает запрошенную проблему… Если бы мне пришлось поместить этот код в один из моих проектов, я бы очень беспокоился об условиях гонки…

 struct Subscriber {  let ids: [Int]  var subscribeCount: Int = 0  let lock = NSRecursiveLock() }  class Publisher {  private let relay = BehaviorRelaylt;[Int: Updates]gt;(value: [:])  private var subscribers: [Subscriber] = []   func updatesStream(for ids: [Int]) -gt; Observablelt;[Updates]gt; {  var subscriber = Subscriber(ids: ids)  let observable = relay  .map { map in  return map  .filter { ids.contains($0.key) }  .map { $0.value }  }  .filter { !$0.isEmpty }  .do(  onSubscribe: {  subscriber.lock.lock()  subscriber.subscribeCount  = 1  subscriber.lock.unlock()  },  onDispose: {  subscriber.lock.lock()  subscriber.subscribeCount -= 1  subscriber.lock.unlock()  })  .asObservable()   subscribers.append(subscriber)  return observable  }   private func repeatTimer() {  subscribers.removeAll(where: { subscriber in  subscriber.subscribeCount == 0  })  } }