Reactive Cocoa

潛在的內(nèi)存泄漏及解決方案

1.一定使用@weakify和@strongify
在block里沒(méi)有很直觀的看到self,但是RACObserve的定義里面卻用到了self。
2.使用ReactiveCocoa必須要保證信號(hào)發(fā)送完成或者發(fā)送錯(cuò)誤。

冷熱信號(hào)

RACSubject 及其子類是熱信號(hào)。
RACSignal 排除 RACSubject 類以外的是冷信號(hào)。

冷信號(hào)轉(zhuǎn)化成熱信號(hào)

- (RACMulticastConnection *)publish;
- (RACMulticastConnection *)multicast:(RACSubject *)subject;
- (RACSignal *)replay;
- (RACSignal *)replayLast;
- (RACSignal *)replayLazily;
- (RACMulticastConnection *)multicast:(RACSubject *)subject {
    [subject setNameWithFormat:@"[%@] -multicast: %@", self.name, subject.name];
    RACMulticastConnection *connection = [[RACMulticastConnection alloc] initWithSourceSignal:self subject:subject];
    return connection;
}

/// implementation RACMulticastConnection

- (id)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject {
    NSCParameterAssert(source != nil);
    NSCParameterAssert(subject != nil);

    self = [super init];
    if (self == nil) return nil;

    _sourceSignal = source;
    _serialDisposable = [[RACSerialDisposable alloc] init];
    _signal = subject;

    return self;
}

#pragma mark Connecting

- (RACDisposable *)connect {
    BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0, 1, &_hasConnected);

    if (shouldConnect) {
        self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal];
    }

    return self.serialDisposable;
}

- (RACSignal *)autoconnect {
    __block volatile int32_t subscriberCount = 0;

    return [[RACSignal
        createSignal:^(id<RACSubscriber> subscriber) {
            OSAtomicIncrement32Barrier(&subscriberCount);

            RACDisposable *subscriptionDisposable = [self.signal subscribe:subscriber];
            RACDisposable *connectionDisposable = [self connect];

            return [RACDisposable disposableWithBlock:^{
                [subscriptionDisposable dispose];

                if (OSAtomicDecrement32Barrier(&subscriberCount) == 0) {
                    [connectionDisposable dispose];
                }
            }];
        }]
        setNameWithFormat:@"[%@] -autoconnect", self.signal.name];
}
  1. 當(dāng)RACSignal類的實(shí)例調(diào)用- (RACMulticastConnection *)multicast:(RACSubject *)subject時(shí),以self和subject作為構(gòu)造參數(shù)創(chuàng)建一個(gè)RACMulticastConnection實(shí)例。
  2. RACMulticastConnection構(gòu)造的時(shí)候,保存source和subject作為成員變量,創(chuàng)建一個(gè)RACSerialDisposable對(duì)象,用于取消訂閱。
  3. 當(dāng)RACMulticastConnection類的實(shí)例調(diào)用- (RACDisposable *)connect這個(gè)方法的時(shí)候,判斷是否是第一次。如果是的話用_signal這個(gè)成員變量來(lái)訂閱sourceSignal之后返回self.serialDisposable;否則直接返回self.serialDisposable。這里面訂閱sourceSignal是重點(diǎn)。
  4. RACMulticastConnection的signal只讀屬性,就是一個(gè)熱信號(hào),訂閱這個(gè)熱信號(hào)就避免了各種副作用的問(wèn)題。它會(huì)在- (RACDisposable *)connect第一次調(diào)用后,根據(jù)sourceSignal的訂閱結(jié)果來(lái)傳遞事件。
  5. 想要確保第一次訂閱就能成功訂閱sourceSignal,可以使用- (RACSignal *)autoconnect這個(gè)方法,它保證了第一個(gè)訂閱者觸發(fā)sourceSignal的訂閱,也保證了當(dāng)返回的信號(hào)所有訂閱者都關(guān)閉連接后sourceSignal被正確關(guān)閉連接。
- (RACMulticastConnection *)publish {
    RACSubject *subject = [[RACSubject subject] setNameWithFormat:@"[%@] -publish", self.name];
    RACMulticastConnection *connection = [self multicast:subject];
    return connection;
}

- (RACSignal *)replay {
    RACReplaySubject *subject = [[RACReplaySubject subject] setNameWithFormat:@"[%@] -replay", self.name];

    RACMulticastConnection *connection = [self multicast:subject];
    [connection connect];

    return connection.signal;
}

- (RACSignal *)replayLast {
    RACReplaySubject *subject = [[RACReplaySubject replaySubjectWithCapacity:1] setNameWithFormat:@"[%@] -replayLast", self.name];

    RACMulticastConnection *connection = [self multicast:subject];
    [connection connect];

    return connection.signal;
}

- (RACSignal *)replayLazily {
    RACMulticastConnection *connection = [self multicast:[RACReplaySubject subject]];
    return [[RACSignal
        defer:^{
            [connection connect];
            return connection.signal;
        }]
        setNameWithFormat:@"[%@] -replayLazily", self.name];
}
  1. - (RACMulticastConnection *)publish就是幫忙創(chuàng)建了RACSubject。
  2. - (RACSignal *)replay就是用RACReplaySubject來(lái)作為subject,并立即執(zhí)行connect操作,返回connection.signal。其作用是上面提到的replay功能,即后來(lái)的訂閱者可以收到歷史值。
  3. - (RACSignal *)replayLast就是用Capacity為1的RACReplaySubject來(lái)替換- (RACSignal *)replay的`subject。其作用是使后來(lái)訂閱者只收到最后的歷史值。
  4. - (RACSignal *)replayLazily- (RACSignal *)replay的區(qū)別就是replayLazily會(huì)在第一次訂閱的時(shí)候才訂閱sourceSignal。

Comparing replay, replayLast, and replayLazily

  __block int num = 0;
  RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id  subscriber) {
      num++;
      NSLog(@"Increment num to: %i", num);
      [subscriber sendNext:@(num)];
      return nil;
  }];
 
  NSLog(@"Start subscriptions");
 
  // Subscriber 1 (S1)
  [signal subscribeNext:^(id x) {
      NSLog(@"S1: %@", x);
  }];
 
  // Subscriber 2 (S2)
  [signal subscribeNext:^(id x) {
      NSLog(@"S2: %@", x);
  }];
 
  // Subscriber 3 (S3)
  [signal subscribeNext:^(id x) {
      NSLog(@"S3: %@", x);
  }];
  Start subscriptions
  Increment num to: 1
  S1: 1
  Increment num to: 2
  S2: 2
  Increment num to: 3
  S3: 3

a normal RACSignal can be thought of as lazy, as it doesn’t do any work until it has a subscriber.

[RACSignal createSignal:^RACDisposable *(id subscriber) { return nil; }];

Cold Signal,需要 subscribeNext: 觸發(fā)。且每次都會(huì)執(zhí)行 subscription code。

  RACSubject *letters = [RACSubject subject];
  RACSignal *signal = letters;
 
  NSLog(@"Subscribe S1");
  [signal subscribeNext:^(id x) {
      NSLog(@"S1: %@", x);
  }];
 
  NSLog(@"Send A");
  [letters sendNext:@"A"];
  NSLog(@"Send B");
  [letters sendNext:@"B"];
 
  NSLog(@"Subscribe S2");
  [signal subscribeNext:^(id x) {
      NSLog(@"S2: %@", x);
  }];
 
  NSLog(@"Send C");
  [letters sendNext:@"C"];
  NSLog(@"Send D");
  [letters sendNext:@"D"];
 
  NSLog(@"Subscribe S3");
  [signal subscribeNext:^(id x) {
      NSLog(@"S3: %@", x);
  }];
Subscribe S1
Send A
S1: A
Send B
S1: B
 
Subscribe S2
Send C
S1: C
S2: C
 
Send D
S1: D
S2: D
Subscribe S3

Our second example shows how each subscriber only receives the values that are sent after their subscription is added.

RACSignal *signal = [RACSubject subject];

新增的 subscribe 不同步之前的信號(hào)

  __block int num = 0;
  RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id  subscriber) {
      num++;
      NSLog(@"Increment num to: %i", num);
      [subscriber sendNext:@(num)];
      return nil;
  }] replay];
 
  NSLog(@"Start subscriptions");
 
  // Subscriber 1 (S1)
  [signal subscribeNext:^(id x) {
      NSLog(@"S1: %@", x);
  }];
 
  // Subscriber 2 (S2)
  [signal subscribeNext:^(id x) {
      NSLog(@"S2: %@", x);
  }];
 
  // Subscriber 3 (S3)
  [signal subscribeNext:^(id x) {
      NSLog(@"S3: %@", x);
  }];
  Increment num to: 1//這里會(huì)直接執(zhí)行一次 subscription code 
  Start subscriptions
  S1: 1
  S2: 1
  S3: 1

This example shows how the subscription code is not re-executed upon new subscriptions.
This time the num integer is incremented immediately, before there are even any subscribers. And it is only incremented once, meaning that the subscription code is only been executed a single time, regardless of how many subscribers the signal has.

[[RACSignal createSignal:^RACDisposable *(id subscriber) { return nil; }] replay]; [[RACSignal createSignal:^RACDisposable *(id subscriber) { return nil; }] replayLast];

replay 與 replayLast 會(huì)生成 Hot Signal, 直接執(zhí)行 subscription code ,且只執(zhí)行一次。

  RACSubject *letters = [RACSubject subject];
  RACSignal *signal = [letters replay];
 
  NSLog(@"Subscribe S1");
  [signal subscribeNext:^(id x) {
      NSLog(@"S1: %@", x);
  }];
 
  NSLog(@"Send A");
  [letters sendNext:@"A"];
  NSLog(@"Send B");
  [letters sendNext:@"B"];
 
  NSLog(@"Subscribe S2");
  [signal subscribeNext:^(id x) {
      NSLog(@"S2: %@", x);
  }];
 
  NSLog(@"Send C");
  [letters sendNext:@"C"];
  NSLog(@"Send D");
  [letters sendNext:@"D"];
 
  NSLog(@"Subscribe S3");
  [signal subscribeNext:^(id x) {
      NSLog(@"S3: %@", x);
  }];
Subscribe S1

Send A
S1: A
Send B
S1: B
 
Subscribe S2
S2: A
S2: B
 
Send C
S1: C
S2: C
Send D
S1: D
S2: D
 
Subscribe S3
S3: A
S3: B
S3: C
S3: D

This example shows how each new subscriber receives the full history of the signal.
Even though S3 subscribed after all of the values had been sent, it still received all of the values.

RACSignal *signal = [[RACSubject subject] replay]; RACSignal *signal = [[RACSubject subject] replayLasily];

replay 與 replayLasily 每次添加 subscribe 會(huì)同步所有歷史信號(hào)。

  RACSubject *letters = [RACSubject subject];
  RACSignal *signal = [letters replayLast];
 
  NSLog(@"Subscribe S1");
  [signal subscribeNext:^(id x) {
      NSLog(@"S1: %@", x);
  }];
 
  NSLog(@"Send A");
  [letters sendNext:@"A"];
  NSLog(@"Send B");
  [letters sendNext:@"B"];
 
  NSLog(@"Subscribe S2");
  [signal subscribeNext:^(id x) {
      NSLog(@"S2: %@", x);
  }];
 
  NSLog(@"Send C");
  [letters sendNext:@"C"];
  NSLog(@"Send D");
  [letters sendNext:@"D"];
 
  NSLog(@"Subscribe S3");
  [signal subscribeNext:^(id x) {
      NSLog(@"S3: %@", x);
  }];
Subscribe S1
 
Send A
S1: A
Send B
S1: B
 
Subscribe S2
S2: B
 
Send C
S1: C
S2: C
Send D
S1: D
S2: D
 
Subscribe S3
S3: D

This example illustrates how only the most recent value is provided to a new subscriber.

RACSignal *signal = [[RACSubject subject] replayLast];

只同步最近一次的歷史信號(hào)

  __block int num = 0;
  RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id  subscriber) {
      num++;
      NSLog(@"Increment num to: %i", num);
      [subscriber sendNext:@(num)];
      return nil;
  }] replayLazily];
 
  NSLog(@"Start subscriptions");
 
  // Subscriber 1 (S1)
  [signal subscribeNext:^(id x) {
      NSLog(@"S1: %@", x);
  }];
 
  // Subscriber 2 (S2)
  [signal subscribeNext:^(id x) {
      NSLog(@"S2: %@", x);
  }];
 
  // Subscriber 3 (S3)
  [signal subscribeNext:^(id x) {
      NSLog(@"S3: %@", x);
  }];
Start subscriptions
Increment num to: 1//添加 subscribe 后才觸發(fā)執(zhí)行 subscription code
S1: 1
S2: 1
S3: 1

The difference between -replayLazily and -replay is that -replayLazily will not subscribe to the source signal until something subscribes to the newly created signal. This is opposed to the behavior of -replay and -replayLast, which subscribe to the source signal immediately upon being called.

[[RACSignal createSignal:^RACDisposable *(id subscriber) { return nil; }] replayLasily];

Cold Signal ,需要觸發(fā)才執(zhí)行 subscription code

參考文獻(xiàn)

replay-replaylast-replaylazily
美團(tuán)技術(shù)博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容