Спиране на публикуването, когато няма абонати и автоматично стартиране, когато има абонати

Как бих внедрил 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 по-горе, но това поведение беше променено защото прави страничните ефекти изключително непредвидими. Този случай на използване трябва да е безопасен, но има и други моменти (като когато правите мрежова заявка или изпълнявате команда на shell), когато автоматичното повторно свързване би било ужасно.

person Justin Spahr-Summers    schedule 08.02.2013
comment
Благодаря. внедри го в github .com/prabirshrestha/reactive-cocoa-playground/blob/ - person prabir; 10.02.2013