ReactiveCocoa2 源码浅析(下)

6、再次改进NLSubscriber

a.didSubscribeWithDisposable

1
2
3
4
@protocol RACSubscriber
@required
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;
@end

这个 RACSubscriber 协议中声明的一个方法,在最开始的时候被我们特意给忽略,现在是时候回过头来看看它了。对于一个订阅者来说,next、error 和 completed 三种事件分别对应协议里的三种方法,那么这个方法存在的意义是什么呢?

从 RACSubscriber 协议中,可以看到,当一个订阅者有收到过 error 或 completed 事件后,这个订阅者就不能再接收任何事件了,换句话说,此时这个订阅者会解除所有的订阅关系,且无法再次订阅。既然要解除所有订阅,首先我得知道我订阅过哪些信号是不?而代表一个订阅行为的就是 disposable ,告诉它就传一个给它好了。所以这个方法就是告诉订阅者:你发生了订阅行为。

那为啥要 RACCompoundDisposable 类型作为参数呢?因为有些订阅者会针对其附加一些操作,而只有这个类型的 disposable 才能动态加入一些操作。接下来我们就会看到的。

b.NLSubscriber 结合 RACDisposable

这一次改进 NLSubscriber 的目的是让其可以终结自己的订阅能力的功能。同时实现 didSubscribeWithDisposable 方法。千言万语不如实际代码,让我们来一探究竟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#import "NLSubscriber.h"
#import @interface NLSubscriber ()
@property (nonatomic, copy) void (^next)(id value);
@property (nonatomic, copy) void (^error)(NSError *error);
@property (nonatomic, copy) void (^completed)(void);
/**
  *  @brief  代表订阅者本身总体订阅行为的 disposable。
  *       当有接收到 error 或 completed 事件时,应该 dispose 这个 disposable。
  */
@property (nonatomic, strong, readonly) RACCompoundDisposable *disposable;
@end
@implementation NLSubscriber
#pragma mark Lifecycle
+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
   NLSubscriber *subscriber = [[self alloc] init];
   subscriber->_next = [next copy];
   subscriber->_error = [error copy];
   subscriber->_completed = [completed copy];
   return  subscriber;
}
- (instancetype)init {
   self = [ super  init];
   if  (self == nil)  return  nil;
   @unsafeify(self);
   /**
    *  当 _disposable 被发送 dispose 消息时,将 next、error 和 completed 这三个
    *  block 设置为 nil,从而间实现订阅者无法再接收任何事件的功能。
    */
   RACDisposable *selfDisposable = [RACDisposable disposableWithBlock:^{
     @strongify(self);
     @synchronized (self) {
       self.next = nil;
       self.error = nil;
       self.completed = nil;
     }
   }];
   _disposable = [RACCompoundDisposable compoundDisposable];
   [_disposable addDisposable:selfDisposable];
   return  self;
}
- (void)dealloc {
   [self.disposable dispose];
}
#pragma mark RACSubscriber
- (void)sendNext:(id)value {
   @synchronized (self) {
     void (^nextBlock)(id) = [self.next copy];
     if  (nextBlock == nil)  return ;
     nextBlock(value);
   }
}
- (void)sendError:(NSError *)e {
   @synchronized (self) {
     void (^errorBlock)(NSError *) = [self.error copy];
     [self.disposable dispose];
     if  (errorBlock == nil)  return ;
     errorBlock(e);
   }
}
- (void)sendCompleted {
   @synchronized (self) {
     void (^completedBlock)(void) = [self.completed copy];
     [self.disposable dispose];
     if  (completedBlock == nil)  return ;
     completedBlock();
   }
}
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)otherDisposable {
   if  (otherDisposable.disposed)  return ;
   /**
    *  将 otherDisposable 添加到 selfDisposable 中。这样当 selfDisposable 被 dispose 时,
    *  otherDisposable 也能被 dispose。
   
    *  这样,当订阅者接收到 error 或 completed 事件时,就能解除这个订阅者自身的所有订阅行为了。
    */
   RACCompoundDisposable *selfDisposable = self.disposable;
   [selfDisposable addDisposable:otherDisposable];
   @unsafeify(otherDisposable);
   /**
    *  如果这个订阅行为被解除,就将 otherDisposable 从 selfDisposable 中移除。
    *  (我们给 otherDisposable 增加了行为,这也就是参数需要是 RACCompoundDisposable
    *  类型的原因了。当然,其它的订阅者怎么用这个参数就跟其实际的业务相关了。)
    */
   [otherDisposable addDisposable:[RACDisposable disposableWithBlock:^{
     @strongify(otherDisposable);
     [selfDisposable removeDisposable:otherDisposable];
   }]];
}
@end

c.改进类别 nl_Subscription

还记得么?nl_Subscription 类别中的订阅方法一旦订阅,就无法停止了,这显然有很大的问题。解决这个问题很简单,直接将 disposable 返回即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// .h file
@interface RACSignal (nl_Subscription)
...
- (RACDisposable *)nl_subscribeError:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock;
 
@end
 
// .m file
@implementation RACSignal (nl_Subscription)
...
- (RACDisposable *)nl_subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock {
   NLSubscriber *subscriber = [NLSubscriber subscriberWithNext:nextBlock error:errorBlock completed:completedBlock];
   return  [self subscribe:subscriber];
}

RACSignal - Operations

本节主要研究这些操作(Operations) —— flattenMap:、map:、 filter: ….

终于看到你想看的东西了?好吧,我承认,上节的东西很无趣,可能压根不是你想看的东西。但如果没弄清上面的内容的话,直接研究 Operations 可是会比较吃力的哟~

你以为咱们现在开始研究 Operations?哈哈,你又得失望了~ 咱得先看看这两个类:RACEmptySignal 和 RACReturnSignal。

1、两个 RACSignal 的特殊子类 RACEmptySignal 和 RACReturnSignal

a.RACEmptySignal

RACEmptySignal 是 +[RACSignal empty] 的内部实现,一个私有 RACSignal 子类。它就是一个会立即 completed 的信号。让我们来看看它的 - subscribe: 方法:

1
2
3
4
5
6
7
8
9
10
- (RACDisposable *)subscribe:(id)subscriber {
     NSCParameterAssert(subscriber != nil);
     
     /**
      *  只要一订阅,就给 subscriber 发送 completed 事件。
      */
     return  [RACScheduler.subscriptionScheduler schedule:^{
         [subscriber sendCompleted];
     }];
}

这样一个订阅者一订阅就会 completed 信号有什么用呢?稍后揭晓。

b.RACReturnSignal

RACReturnSignal 是 +[RACSignal return:] 的内部实现,也是一个私有 RACSignal 子类。它会同步发送出一个值(即 next)给订阅者,然后再发送 completed 事件。 它比 RACEmptySignal 多了一点点东西,它是。直接看其实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@interface RACReturnSignal ()
// 在本信号被订阅时会发送的值。
@property (nonatomic, strong, readonly) id value;
@end
 
@implementation RACReturnSignal
 
#pragma mark Lifecycle
+ (RACSignal *) return :(id)value {
     RACReturnSignal *signal = [[self alloc] init];
     signal->_value = value;
     return  signal;
}
 
#pragma mark Subscription
 
- (RACDisposable *)subscribe:(id)subscriber {
     NSCParameterAssert(subscriber != nil);
    
     return  [RACScheduler.subscriptionScheduler schedule:^{
         [subscriber sendNext:self.value];
         [subscriber sendCompleted];
     }];
}
 
@end

纯吐槽:为啥要叫 ReturnSignal 呢?不如直接 OneValueSignal 好了。O(∩_∩)O~~ 不过说真的,RAC 的命名真心不咋地。

那么发送一个 next 后又 completed 的信号又有啥用呢?等下会知道地。

b.concat: 练手

-[RACSignal concat:] 是源码较简单,且使用频率也较多的。那咱们就来拿它来练练手好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (RACSignal *)concat:(RACSignal *)signal {
     return  [[RACSignal createSignal:^(id subscriber) {
         RACSerialDisposable *serialDisposable = [[RACSerialDisposable alloc] init];
        
         RACDisposable *sourceDisposable = [self subscribeNext:^(id x) {
             [subscriber sendNext:x];
         } error:^(NSError *error) {
             [subscriber sendError:error];
         } completed:^{
             RACDisposable *concattedDisposable = [signal subscribe:subscriber];
             serialDisposable.disposable = concattedDisposable;
         }];
         
         serialDisposable.disposable = sourceDisposable;
         return  serialDisposable;
     }] setNameWithFormat:@ "[%@] -concat: %@" , self.name, signal];
}

RACSerialDisposable 是 RACDisposable 的子类,它包含一个 Disposable,能够在运行时设置这个 Disposable。当设置新的 newDisposable时,老的 oldDisposable 会被 dispose。当 RACSerialDisposable 被 dispose 时,其所包含的 Disposable 会被 dispose。

基本上,对一个 RACSignal 的操作的返回值是一个新的 RACSignal 值时,其内部都是调用了 +[RACSignal createSignal:] 这个方法。这个创建信号返回的实际是自定义信号:RACDynamicSignal,针对它前文有所介绍。

这里有一个小技巧。因为很多信号的操作是针对该信号本身 self 所发送的值作的操作。那也就是说会订阅 self,那咱们先找到这一句再说:self subscribe: 或 self subscribeNext:...。嗯,找到了这几行:

1
2
3
4
5
6
7
8
RACDisposable *sourceDisposable = [self subscribeNext:^(id x) {
     [subscriber sendNext:x];
} error:^(NSError *error) {
     [subscriber sendError:error];
} completed:^{
     RACDisposable *concattedDisposable = [signal subscribe:subscriber];
     serialDisposable.disposable = concattedDisposable;
}];

在订阅了 self 后,将 next 和 error 事件发送给订阅者 subscriber。当 self 发送了 completed 事件事,再让 subscriber 订阅参数 signal。也就是当源信号完成后订阅 signal。怎么样,很简单吧。

c.zipWith:

再来一个练手的玩意。-[RACSignal zipWith:] 比 -[RACSignal concat:] 稍微复杂点。它是将 self 和 参数 signal 两个信号发送的值合并起来发送给订阅者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
- (RACSignal *)zipWith:(RACSignal *)signal {
     NSCParameterAssert(signal != nil);
     return  [[RACSignal createSignal:^(id subscriber) {
         __block BOOL selfCompleted = NO;
         NSMutableArray *selfValues = [NSMutableArray array];
         __block BOOL otherCompleted = NO;
         NSMutableArray *otherValues = [NSMutableArray array];
         void (^sendCompletedIfNecessary)(void) = ^{
             @synchronized (selfValues) {
                 BOOL selfEmpty = (selfCompleted && selfValues.count == 0);
                 BOOL otherEmpty = (otherCompleted && otherValues.count == 0);
                 if  (selfEmpty || otherEmpty) [subscriber sendCompleted];
             }
         };
         void (^sendNext)(void) = ^{
             @synchronized (selfValues) {
                 if  (selfValues.count == 0)  return ;
                 if  (otherValues.count == 0)  return ;
                 RACTuple *tuple = [RACTuple tupleWithObjects:selfValues[0], otherValues[0], nil];
                 [selfValues removeObjectAtIndex:0];
                 [otherValues removeObjectAtIndex:0];
                 [subscriber sendNext:tuple];
                 sendCompletedIfNecessary();
             }
         };
         RACDisposable *otherDisposable = [signal subscribeNext:^(id x) {
             @synchronized (selfValues) {
                 [otherValues addObject:x ?: RACTupleNil.tupleNil];
                 sendNext();
             }
         } error:^(NSError *error) {
             [subscriber sendError:error];
         } completed:^{
             @synchronized (selfValues) {
                 otherCompleted = YES;
                 sendCompletedIfNecessary();
             }
         }];
         return  [RACDisposable disposableWithBlock:^{
             [selfDisposable dispose];
             [otherDisposable dispose];
         }];
     }] setNameWithFormat:@ "[%@] -zipWith: %@" , self.name, signal];
}

同样的,重点在 [self subscriberNext:] 和 [signal subscribeNext:] 处。这里的实现是订阅 self 和 signal 信号,然后将它们发送出的值收集起来,当两个都发出了值时,分别拿出两个信号最早发出的值,合并为一个 RACTuple,再发送给订阅者 subscriber。这个也很简单吧,只是代码稍多点而已。

d.bind:

(i)说明

信号的很多 operations 的实现调用来调用去最后都是调用了这个 -[RACSignal bind:] 方法,比如 flattenMap: 、map:、filter 等等。那咱们就来看看这个方法是哪路神仙?

这是在 RACStream 中声明的抽象方法。来看看它的声明:

1
2
typedef RACStream * (^RACStreamBindBlock)(id value, BOOL *stop);
- (instancetype)bind:(RACStreamBindBlock (^)(void))block;

RACStreamBindBlock 是一个 block。它从一个 RACStream 中接收一个值,并且返回一个与该流相同类型的实例。如果将 stop 设为 YES,则会在返回一个实例后终结此次 bind。如果返回 nil 则会立即终结。

bind: 方法是将流中每一个值都放到 RACStreamBindBlock 中跑一下。来看看其参数:block。然而这有什么卵用呢?好吧,我太笨,从它的说明来看,我真的不能理解它有什么用。

(ii)源码解读

既然从方法说明了解不到,那直接来看其源码了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
- (RACSignal *)bind:(RACStreamBindBlock (^)(void))block {
     NSCParameterAssert(block != NULL);
     return  [[RACSignal createSignal:^(id subscriber) {
         RACStreamBindBlock bindingBlock = block();
         NSMutableArray *signals = [NSMutableArray arrayWithObject:self];
         RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
         // 三.
         void (^completeSignal)(RACSignal *, RACDisposable *) = ^(RACSignal *signal, RACDisposable *finishedDisposable) {
             BOOL removeDisposable = NO;
             @synchronized (signals) {
                 [signals removeObject:signal];
                 if  (signals.count == 0) {
                     [subscriber sendCompleted];
                     [compoundDisposable dispose];
                 else  {
                     removeDisposable = YES;
                 }
             }
             if  (removeDisposable) [compoundDisposable removeDisposable:finishedDisposable];
         };
         // 二.
         void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
             @synchronized (signals) {
                 [signals addObject:signal];
             }
             RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
             [compoundDisposable addDisposable:selfDisposable];
             RACDisposable *disposable = [signal subscribeNext:^(id x) {
                 [subscriber sendNext:x];
             } error:^(NSError *error) {
                 [compoundDisposable dispose];
                 [subscriber sendError:error];
             } completed:^{
                 @autoreleasepool {
                     completeSignal(signal, selfDisposable);
                 }
             }];
             selfDisposable.disposable = disposable;
         };
         @autoreleasepool {
             RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
             [compoundDisposable addDisposable:selfDisposable];
            //  一.
             RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
                 // Manually check disposal to handle synchronous errors.
                 if  (compoundDisposable.disposed)  return ;
                 BOOL stop = NO;
                 id signal = bindingBlock(x, &stop);
                 @autoreleasepool {
                     if  (signal != nil) addSignal(signal);
                     if  (signal == nil || stop) {
                         [selfDisposable dispose];
                         completeSignal(self, selfDisposable);
                     }
                 }
             } error:^(NSError *error) {
                 [compoundDisposable dispose];
                 [subscriber sendError:error];
             } completed:^{
                 @autoreleasepool {
                     completeSignal(self, selfDisposable);
                 }
             }];
             selfDisposable.disposable = bindingDisposable;
         }
         return  compoundDisposable;
     }] setNameWithFormat:@ "[%@] -bind:" , self.name];
}

我们一步一步来看。先从第 一 步开始,其步骤如下:

  • 订阅 self

  • 针对 self 发出的每一个值 x,经过 bindingBlock,获取一个信号:signal

    1. 如果 signal 不为 nil,就转到第二步:addSignal

    2. 如果 signal 为 nil,或 stop 为 YES,则转到第三步:completedSignal

    3. 如果 self 发出 error 事件,则中断订阅;如果 self 发出 completed 事件则转到第三步:completedSignal

第二步:addSignal:signal

  • 先将 signal 添加到 signals中

  • 订阅 signal

    1. 将 signal 的 next 事件转发给订阅者 subscriber

    2. 如果 signal 发送 error 事件则中断订阅

    3. 如果 signal 发送 complete 事件,则转到第三步

第三步:completeSignal:signal:disposable

  • 将 signal 从 signals 中移除

  • 如果 signals 中没有了 signal,那么订阅就完成了

好了,来总结一下这个 -bind: :

  • 订阅原信号 self 的 values。

  • 将 self 发出的任何一个值,都对其使用 bindingBlock 进行转换。

  • 如果 bindingBlock 返回一个信号,则订阅它,将从它那接收到的每个值都传递给订阅者 subscriber。

  • 如果 bindingBlock 要求结束绑定,则 complete self 信号。

  • 如果 所有 的信号全都 complete,则给 subscriber 发送 completed 事件.

  • 如果任何一个信号发出 error,将其发送给 subscriber。

那从中可以玩出什么花样呢?

(iii)示例

咱们先用用它,再看看能怎么玩吧。

示例1:结合 RACReturnSignal

1
2
3
4
5
6
7
8
9
10
RACSignal *signalInterval = [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] take:3];
   RACSignal *bindSignal = [signalInterval bind:^RACStreamBindBlock{
     return  ^(id value, BOOL *stop) {
       NSLog(@ "inner value: %@" , value);
       return  [RACSignal  return :value];
     };
   }];
   [bindSignal subscribeNext:^(id x) {
     NSLog(@ "outer value: %@" , x);
   }];

输出如下:

1
2
3
4
5
6
2015-08-27 17:16:17.933 RACPraiseDemo[3063:168556] inner value: 2015-08-27 09:16:17 +0000
2015-08-27 17:16:17.934 RACPraiseDemo[3063:168556] outer value: 2015-08-27 09:16:17 +0000
2015-08-27 17:16:18.931 RACPraiseDemo[3063:168556] inner value: 2015-08-27 09:16:18 +0000
2015-08-27 17:16:18.931 RACPraiseDemo[3063:168556] outer value: 2015-08-27 09:16:18 +0000
2015-08-27 17:16:19.931 RACPraiseDemo[3063:168556] inner value: 2015-08-27 09:16:19 +0000
2015-08-27 17:16:19.931 RACPraiseDemo[3063:168556] outer value: 2015-08-27 09:16:19 +0000

这个示例就是在 bind: 中简单的返回值。那咱们将这个值变化一下如何?

示例2:结合 RACReturnSignal、转换 value

1
2
3
4
5
6
7
8
9
10
11
RACSignal *signalInterval = [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] take:3];
   RACSignal *bindSignal = [signalInterval bind:^RACStreamBindBlock{
     return  ^(NSDate *value, BOOL *stop) {
       NSLog(@ "inner value: %@" , value);
       NSTimeInterval nowTime = [value timeIntervalSince1970];
       return  [RACSignal  return :@(nowTime)];
     };
   }];
   [bindSignal subscribeNext:^(id x) {
     NSLog(@ "outer value: %@" , x);
   }];

输出如下:

1
2
3
4
5
6
2015-08-27 17:34:04.938 RACPraiseDemo[3153:176383] inner value: 2015-08-27 09:34:04 +0000
2015-08-27 17:34:04.939 RACPraiseDemo[3153:176383] outer value: 1440668044.936496
2015-08-27 17:34:05.939 RACPraiseDemo[3153:176383] inner value: 2015-08-27 09:34:05 +0000
2015-08-27 17:34:05.939 RACPraiseDemo[3153:176383] outer value: 1440668045.939163
2015-08-27 17:34:06.941 RACPraiseDemo[3153:176383] inner value: 2015-08-27 09:34:06 +0000
2015-08-27 17:34:06.941 RACPraiseDemo[3153:176383] outer value: 1440668046.941275

哇哇,这就是个 map: 有木有? 现在,有感受到 RACReturnSignal 的魅力?RACReturnSignal 和 -bind: 结合能转换 value。

示例3:结合 RACEmptySignal

现在来换个玩法试试看,这回换 RACEmptySignal 来玩玩。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
RACSignal *signalInterval = [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] take:3];
   __block NSUInteger count = 0;
   RACSignal *bindSignal = [signalInterval bind:^RACStreamBindBlock{
     return  ^(NSDate *value, BOOL *stop) {
       NSLog(@ "inner value: %@" , value);
       ++count;
       if  (count % 2 == 0) {
         return  [RACSignal empty];
       }
       return  [RACSignal  return :value];
     };
   }];
   [bindSignal subscribeNext:^(id x) {
     NSLog(@ "outer value: %@" , x);
   }];

输出如下:

1
2
3
4
5
2015-08-27 17:53:45.345 RACPraiseDemo[3363:188270] inner value: 2015-08-27 09:53:45 +0000
2015-08-27 17:53:45.346 RACPraiseDemo[3363:188270] outer value: 2015-08-27 09:53:45 +0000
2015-08-27 17:53:46.345 RACPraiseDemo[3363:188270] inner value: 2015-08-27 09:53:46 +0000
2015-08-27 17:53:47.342 RACPraiseDemo[3363:188270] inner value: 2015-08-27 09:53:47 +0000
2015-08-27 17:53:47.342 RACPraiseDemo[3363:188270] outer value: 2015-08-27 09:53:47 +0000

这一次,“outer value” 比 “inner value” 少了一个,这就是 filter: 呀!RACEmptySignal 与 bind: 结合能过滤 value。

示例4:改进 bind:

经过这几个示例,我们可以发现,直接使用 bind: 是比较麻烦的。而一般情况下,咱们还真用不到 stop,那咱们就改进一下呗:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (instancetype)flattenMap:(RACStream * (^)(id value))block {
     Class class = self.class;
     return  [[self bind:^{
         return  ^(id value, BOOL *stop) {
             /**
              *  如果 block 返回 nil,得用 RACEmptySignal 代替,
              *  不然会结束 `bind:`
              */
             id stream = block(value) ?: [class empty];
             NSCAssert([stream isKindOfClass:RACStream.class], @ "Value returned from -flattenMap: is not a stream: %@" , stream);
             return  stream;
         };
     }] setNameWithFormat:@ "[%@] -flattenMap:" , self.name];
}

哈哈,这个就是 - flattenMap:了。不必过多解释了吧~

(iv)-map:

嗯,这其实就是 -flattenMap: 与 RACReturnSignal 的结合:

1
2
3
4
5
6
7
- (instancetype)map:(id (^)(id value))block {
     NSCParameterAssert(block != nil);
     Class class = self.class;
     return  [[self flattenMap:^(id value) {
         return  [class  return :block(value)];
     }] setNameWithFormat:@ "[%@] -map:" , self.name];
}

(v)-flatten

信号可以发送类型的值,当然也包括 RACSignal 类型。例如,RACCommand 的 executionSignals 这个信号,它发出的值就是 RACSignal 类型的。对于这种发出的值是 RACSignal 类型的 RACSignal,叫做 signal of signals。这有点类似于 disposable of disposables。

既然这个信号发出的就是 RACSignal,那在 -flattenMap:中,我们直接将 value 返回就好了。来看看示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
    *  有效三次的间隔为1秒定时器信号,此时 signalInterval 是一个 signal of NSDates
    */
   RACSignal *signalInterval = [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] take:3];
   /**
    *  将定时器信号里的值修改成 RACSignal 类型
    *  此时,signalInterval 变成了一个 signal of signals
    */
   signalInterval = [signalInterval map:^id(NSDate *date) {
     return  [RACSignal  return :date];
   }];
   /**
    *  既然 signalInterval 里的值都是信号,那直接将这些信号返回即可
    */
   RACSignal *signal = [signalInterval flattenMap:^RACStream *(RACSignal *returnSignal) {
     return  returnSignal;
   }];
   /**
    *  由于 signalInterval 里的值都是包含了一个 NSDate 值的 RACReturnSignal,
    *  经过 `-flattenMap:` 过后,signal 就变成了 signal of NSDates。
    */
   [signal subscribeNext:^(id x) {
     NSLog(@ "value: %@" , x);
   }];

输出如下:

1
2
3
2015-08-27 21:16:29.517 RACPraiseDemo[549:11996] value: 2015-08-27 13:16:29 +0000
2015-08-27 21:16:30.516 RACPraiseDemo[549:11996] value: 2015-08-27 13:16:30 +0000
2015-08-27 21:16:31.516 RACPraiseDemo[549:11996] value: 2015-08-27 13:16:31 +0000

(vi)小结

RACSignal 的 operations 实在太多,全部在这里列出来不现实,也没有这个必要。我相信,经过前面的解析,你现在再去看其它 的一个 operation 源码,也应该不是太大的难事。

RAC() 宏展开

RAC 的最大的魅力之一就是绑定:RAC(self, ...) = signal; 这应该是大家经常写的一条语句。有没有想过它是怎么工作的呢?咱们来看点代码:

1
2
3
4
5
6
7
@property (nonatomic, strong) NSString *text;
//--------
RACSignal *signal = [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] take:3];
   signal = [signal map:^id(id value) {
     return  [value description];
   }];
RAC(self, text) = signal;

重点在 RAC(self, text) = signal; 这一行。先来看看将这个宏展开是什么样子(RAC 对宏的运用很是牛B,有兴趣请看这篇文章):

1
[[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(self) nilValue:nil][@ "text" ] = signal;

看得更清楚一点:

1
2
RACSubscriptingAssignmentTrampoline *assignment = [[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(self) nilValue:nil];
assignment[@ "text" ] = signal;

跳到 RACSubscriptingAssignmentTrampoline 类的声明,可以看到:

1
2
3
4
5
6
@interface RACSubscriptingAssignmentTrampoline : NSObject
- (id)initWithTarget:(id)target nilValue:(id)nilValue;
 
// 这是可以使用下标 `[]` 语法的关键
- (void)setObject:(RACSignal *)signal forKeyedSubscript:(NSString *)keyPath;
@end

这个类使用了 clang 的特性,可以使用 []语法([] 的相关文章)。也就是说 assignment[@"text"] = signal;,实际上是这样子的:

1
[assignment setObject:signal forKeyedSubscript:@ "text" ];

再看 - (void)setObject:(RACSignal *)signal forKeyedSubscript:(NSString *)keyPath; 这个方法的实现,我们发现,它其实调用的是 signal 的方法:-

1
2
3
4
5
6
7
8
- (RACDisposable *)setKeyPath:(NSString *)keyPath onObject:(NSObject *)object nilValue:(id)nilValue {
     ...
     RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) {
         ...
         [object setValue:x ?: nilValue forKeyPath:keyPath];
     }...
     ...
}

哦,原来它就是订阅了 signal,并将 signal 发出的每一值都设置给 object 的 keyPath 属性而已。很简单嘛~

结束

本文研究了 RAC 中的一些基本组件,并没有对一些高级内容进行深入研究,所以才叫“浅析”。但这些也是对高级内容深入研究的基础,既然有“渔”,何惧无“鱼”呢?

其实颇想继续分享,但心有余而力不足。

还可研究的主题:

  • Subjects 它也是 RACSignal 一些操作的基础,值得研究。难度系数:2 (最高为5)

  • RACMulticastConnection 常用,值得研究。难度系数:3

  • Foundation、UIKit、KVO (给各系统类加的 rac_ 扩展),有研究价值。研究过后,你会对 runtime 会有很深入的了解,还会接触到一些 OC 中少用的知识(如 NSProxy 等),能开拓视野。难度系数:5

难度系数是本人 YY 出来的,别较真,仅当参考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值