Остановить публикацию, когда нет подписчиков, и автоматически запустить, когда есть подписчики.

Как мне реализовать RACSignal, который прекращал бы публикацию, когда на него нет подписчиков, и автоматически запускался бы, когда есть подписчики?

Вот сценарий:

Допустим, у меня есть currentLocationSignal в AppDelegate. Мой LocationViewController будет подписываться на currentLocationSignal при загрузке представления и отписываться (утилизировать) при выгрузке представления. Поскольку для получения текущего местоположения требуется несколько секунд, я хотел бы всегда подписываться на currentLocationSignal при открытии приложения (и автоматически отменять подписку через несколько секунд), поэтому к тому времени, когда я прибуду в LocationViewController, я получу точное местоположение. Таким образом, на сигнал может быть больше одного подписчика. Когда первый абонент прослушает, ему нужно начать звонить startUpdatingLocation, а когда абонентов нет, нужно звонить stopUpdatingLocation.


person prabir    schedule 08.02.2013    source источник


Ответы (1)


Хороший вопрос! Обычно вы используете RACMulticastConnection для таких случаев использования, как это, но, поскольку вы хотите, чтобы сигнал мог повторно активироваться позже, соединение само по себе не подходит.

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

Начнем с добавления свойства locationSubject. Тема должна быть RACReplaySubject, потому что мы всегда хотите, чтобы новые подписчики немедленно получали информацию о последнем отправленном местоположении. Внедрение обновлений с этой темой достаточно просто:

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    [self.locationSubject sendNext:locations.lastObject];
}

Затем мы хотим реализовать сигнал, который отслеживает и увеличивает/уменьшает количество подписчиков. Это работает с использованием целочисленного свойства numberOfLocationSubscribers:

- (RACSignal *)currentLocationSignal {
    return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
        @synchronized (self) {
            if (self.numberOfLocationSubscribers == 0) {
                [self.locationManager startUpdatingLocation];
            }

            ++self.numberOfLocationSubscribers;
        }

        [self.locationSubject subscribe:subscriber];

        return [RACDisposable disposableWithBlock:^{
            @synchronized (self) {
                --self.numberOfLocationSubscribers;
                if (self.numberOfLocationSubscribers == 0) {
                    [self.locationManager stopUpdatingLocation];
                }
            }
        }];
    }];
}

В приведенном выше коде блок +createSignal: вызывается каждый раз, когда к возвращаемому сигналу добавляется новый подписчик. Когда это произойдет:

  1. Мы проверяем, не равно ли количество подписчиков в настоящее время нулю. Если это так, только что добавленный подписчик является первым, поэтому нам нужно включить (или повторно включить) обновления местоположения.
  2. Мы подключаем подписчика напрямую к нашему locationSubject, поэтому значения из последнего автоматически передаются в первый.
  3. Затем, в будущем, когда подписка будет удалено, мы уменьшаем значение счетчика и при необходимости останавливаем обновление местоположения.

Теперь осталось подписаться на currentLocationSignal при запуске и автоматически отписаться через несколько секунд:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Use a capacity of 1 because we only ever care about the latest
    // location.
    self.locationSubject = [RACReplaySubject replaySubjectWithCapacity:1];

    [[self.currentLocationSignal
        takeUntil:[RACSignal interval:3]]
        subscribeCompleted:^{
            // We don't actually need to do anything here, but we need
            // a subscription to keep the location updating going for the
            // time specified.
        }];

    return YES;
}

Это немедленно подписывается на self.currentLocationSignal, а затем автоматически удаляет эту подписку, когда сигнал +interval: отправляет свое первое значение.

Интересно, что -[RACMulticastConnection autoconnect] раньше вел себя как -currentLocationSignal выше, но это поведение изменилось потому что это делает побочные эффекты крайне непредсказуемыми. Этот вариант использования должен быть безопасным, но бывают и другие случаи (например, при выполнении сетевого запроса или выполнении команды оболочки), когда автоматическое переподключение было бы ужасным.

person Justin Spahr-Summers    schedule 08.02.2013
comment
Спасибо. реализовал его на github .com/prabirshrestha/reactive-cocoa-playground/blob/ - person prabir; 10.02.2013