RAC-基础笔记

RAC最主要的优势在于提供了一种统一的方式来处理异步行为,包括delegate、回调block、target-action机制、通知和KVO。

RACStream

RAC的核心,一个抽象类,RAC中的信号都继承自这个类。其本身不是非常有用,大部分由signalssequences代替。 其中定义了一系列流操作的方法,实现在子类中重写。 Stream表示一系列对象的值,值可在现在或将来可用,但都是依次检索的。

RACSignal

继承自RACStream,一种_push-driven_的流。通常用来表示将在未来传递的数据。 冷信号,当订阅的时候才发送消息。当多次订阅,信号体内代码将会多次执行。 任何的信号转换即是对原有的信号进行订阅从而产生新的信号

创建信号

    // 创建信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"发送信号"];
        [subscriber sendCompleted];
        // 错误和结束发送只能二选一
        // [subscriber sendError:[NSError errorWithDomain:@"RACDemo" code:110 userInfo:@{NSLocalizedDescriptionKey : @"错误"}]];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"-------------->disposable");
        }];
    }];

订阅信号

    // 订阅信号
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------------->第一次订阅:%@", x);
    }];

RACSubject

即能充当信号,也能发送信号,是一个可以手动控制的信号。 热信号,即使未订阅也会发送信号。当订阅信号后,只会收到订阅时间之后的信号。 RACSubject实现了RACSubscriber协议。

@protocol RACSubscriber <NSObject>
@required
- (void)sendNext:(nullable id)value;
- (void)sendError:(nullable NSError *)error;
- (void)sendCompleted;
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;

@end

RACSubject有两个子类:

RACSubject:

    RACSubject *subject = [RACSubject subject];
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------------->RACSubject1:%@",x);
    }];
    [subject sendNext:@"B"];
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------------->RACSubject2:%@",x);
    }];
    [subject sendNext:@"C"];
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------------->RACSubject3:%@",x);
    }];
    [subject sendNext:@"D"];

打印:

2018-01-23 17:48:07.697511+0800 RACLoginDemo[66964:32709151] -------------->RACSubject1:B
2018-01-23 17:48:07.697702+0800 RACLoginDemo[66964:32709151] -------------->RACSubject1:C
2018-01-23 17:48:07.697823+0800 RACLoginDemo[66964:32709151] -------------->RACSubject2:C
2018-01-23 17:48:07.697947+0800 RACLoginDemo[66964:32709151] -------------->RACSubject1:D
2018-01-23 17:48:07.698232+0800 RACLoginDemo[66964:32709151] -------------->RACSubject2:D
2018-01-23 17:48:07.698401+0800 RACLoginDemo[66964:32709151] -------------->RACSubject3:D

RACReplaySubject:

    RACReplaySubject *subject = [RACReplaySubject subject];
    [subject sendNext:@"A"];
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------------->RACReplaySubject1:%@",x);
    }];
    [subject sendNext:@"B"];
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------------->RACReplaySubject2:%@",x);
    }];
    [subject sendNext:@"C"];
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------------->RACReplaySubject3:%@",x);
    }];
    [subject sendNext:@"D"];

打印:

2018-01-23 17:51:26.818273+0800 RACLoginDemo[67096:32734184] -------------->RACReplaySubject1:A
2018-01-23 17:51:26.818514+0800 RACLoginDemo[67096:32734184] -------------->RACReplaySubject1:B
2018-01-23 17:51:26.818648+0800 RACLoginDemo[67096:32734184] -------------->RACReplaySubject2:A
2018-01-23 17:51:26.818806+0800 RACLoginDemo[67096:32734184] -------------->RACReplaySubject2:B
2018-01-23 17:51:26.818958+0800 RACLoginDemo[67096:32734184] -------------->RACReplaySubject1:C
2018-01-23 17:51:26.819078+0800 RACLoginDemo[67096:32734184] -------------->RACReplaySubject2:C
2018-01-23 17:51:26.819276+0800 RACLoginDemo[67096:32734184] -------------->RACReplaySubject3:A
2018-01-23 17:51:26.819377+0800 RACLoginDemo[67096:32734184] -------------->RACReplaySubject3:B
2018-01-23 17:51:26.819482+0800 RACLoginDemo[67096:32734184] -------------->RACReplaySubject3:C
2018-01-23 17:51:26.819677+0800 RACLoginDemo[67096:32734184] -------------->RACReplaySubject1:D
2018-01-23 17:51:26.819795+0800 RACLoginDemo[67096:32734184] -------------->RACReplaySubject2:D
2018-01-23 17:51:26.820147+0800 RACLoginDemo[67096:32734184] -------------->RACReplaySubject3:D

RACBehaviorSubject:

    RACBehaviorSubject *sub = [RACBehaviorSubject behaviorSubjectWithDefaultValue:@"init"];
    [sub subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------------->RACBehaviorSubject1:%@",x);
    }];
    [sub sendNext:@"A"];
    [sub sendNext:@"B"];
    
    [sub subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------------->RACBehaviorSubject2:%@",x);
    }];
    [sub sendNext:@"C"];

打印:

2018-01-23 17:53:35.242953+0800 RACLoginDemo[67192:32750779] -------------->RACBehaviorSubject1:init
2018-01-23 17:53:35.243233+0800 RACLoginDemo[67192:32750779] -------------->RACBehaviorSubject1:A
2018-01-23 17:53:35.243373+0800 RACLoginDemo[67192:32750779] -------------->RACBehaviorSubject1:B
2018-01-23 17:53:35.243570+0800 RACLoginDemo[67192:32750779] -------------->RACBehaviorSubject2:B
2018-01-23 17:53:35.243721+0800 RACLoginDemo[67192:32750779] -------------->RACBehaviorSubject1:C
2018-01-23 17:53:35.243831+0800 RACLoginDemo[67192:32750779] -------------->RACBehaviorSubject2:C

RACMulticastConnection

RACMulticastConnection用于将冷信号转换为热信号,用于多播。connection避免直接创建,可以通过RACSignal-publish或者-multicast:创建。 -publish内部实现:

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

使用publish方法,在connect后订阅的订阅者不能收到消息。 eg:

    static NSInteger sendCount = 0;
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        NSString *temp = [NSString stringWithFormat:@"----->发送信号:%ld",++sendCount];
        NSLog(@"%@",temp);
        [subscriber sendNext:temp];
        [subscriber sendCompleted];
        // 错误和结束发送只能二选一
        // [subscriber sendError:[NSError errorWithDomain:@"RACDemo" code:110 userInfo:@{NSLocalizedDescriptionKey : @"错误"}]];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"-------------->disposable");
        }];
    }];
    RACMulticastConnection *connection = [signal publish];
    // 订阅信号
    [connection.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------------->第一次订阅:%@", x);
    }];
    
    [connection.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------------->第二次订阅:%@", x);
    }];
    [connection connect];
    [connection.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------------->第三次订阅:%@", x);
    }];

打印:

2018-01-25 17:50:54.061115+0800 RACLoginDemo[64455:5028191] ----->发送信号:1
2018-01-25 17:50:54.061328+0800 RACLoginDemo[64455:5028191] -------------->第一次订阅:----->发送信号:1
2018-01-25 17:50:54.061457+0800 RACLoginDemo[64455:5028191] -------------->第二次订阅:----->发送信号:1
2018-01-25 17:50:54.061693+0800 RACLoginDemo[64455:5028191] -------------->disposable

如果需要connect之后的订阅者也能收到消息,可以使用RACReplaySubject。 eg:

    省略代码....
    RACReplaySubject *subject = [RACReplaySubject subject];
    RACMulticastConnection *connection = [signal multicast:subject];
    省略代码....

如果有这样的需求可以通过更简单的方法实现,RACSignal提供了三个更便利的方法:

/// Multicasts the signal to a RACReplaySubject of unlimited capacity, and
/// immediately connects to the resulting RACMulticastConnection.
///
/// Returns the connected, multicasted signal.
- (RACSignal<ValueType> *)replay;

/// Multicasts the signal to a RACReplaySubject of capacity 1, and immediately
/// connects to the resulting RACMulticastConnection.
///
/// Returns the connected, multicasted signal.
- (RACSignal<ValueType> *)replayLast;

/// Multicasts the signal to a RACReplaySubject of unlimited capacity, and
/// lazily connects to the resulting RACMulticastConnection.
///
/// This means the returned signal will subscribe to the multicasted signal only
/// when the former receives its first subscription.
///
/// Returns the lazily connected, multicasted signal.
- (RACSignal<ValueType> *)replayLazily;

RACCommand

继承自NSObject,创建并订阅信号以响应某个动作。可以结合UIControl一起使用。

初始化

初始化1:

- (instancetype)initWithEnabled:(nullable RACSignal<NSNumber *> *)enabledSignal signalBlock:(RACSignal<ValueType> * (^)(InputType _Nullable input))signalBlock;

初始化2:多传入的enableSignal为nil。

- (instancetype)initWithSignalBlock:(RACSignal<ValueType> * (^)(InputType _Nullable input))signalBlock;

eg:

    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            // 可以用来封装网络请求。
            // 如果不sendCompleted将不会再相应
            NSLog(@"-------------->RACSignal:%@",input);
            [subscriber sendNext:input];
            [subscriber sendCompleted];
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"-------------->disposable");
            }];
        }];
    }];
    [[command.executionSignals switchToLatest] subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------------->getData:%@",x);
    }];
    [command execute:@"inputData"];

打印:

2018-01-25 16:56:34.138045+0800 RACLoginDemo[63214:4720098] -------------->RACSignal:inputData
2018-01-25 16:56:34.138297+0800 RACLoginDemo[63214:4720098] -------------->getData:inputData
2018-01-25 16:56:34.138650+0800 RACLoginDemo[63214:4720098] -------------->disposable

RACSequence

继承自RACStream,一种_pull-driven_的流。Sepuence是一种集合,不过其中的数据只有当其使用时才加载。RAC为大部分Cocoa的集合添加了-rac_sequence方法,以便转化为RACSequence使用。 eg:

    RACSequence *results = [[@[@"1",@"22",@"333"].rac_sequence
                             filter:^ BOOL (NSString *str) {
                                 return str.length >= 2;
                             }]
                            map:^(NSString *str) {
                                NSLog(@"-------------->x:%@",str);
                                return [str stringByAppendingString:@"foobar"];
                            }];
    [results.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------------->result:%@",x);
    }];
    NSArray *array = results.array;
    NSLog(@"-------------->array:%@",array);

打印:

2018-01-26 11:38:20.317264+0800 RACLoginDemo[68540:5938859] -------------->x:22
2018-01-26 11:38:20.317512+0800 RACLoginDemo[68540:5938859] -------------->result:22foobar
2018-01-26 11:38:20.317523+0800 RACLoginDemo[68540:5938681] -------------->x:333
2018-01-26 11:38:20.317706+0800 RACLoginDemo[68540:5938859] -------------->result:333foobar
2018-01-26 11:38:20.317771+0800 RACLoginDemo[68540:5938681] -------------->array:(
    22foobar,
    333foobar
)

RACScheduler

RACScheduler类表示的调度程序是一个串行队列,用于执行工作或传递结果信号。 调度程序类似于GCD,但是通过disposables可以取消。也类似于NSOperationQueue,但是不允许重新排序或相互依赖。 除了 +immediateSchedulerScheduler不允许同步执行。

生成Scheduler

+ (RACScheduler *)immediateScheduler;:立即执行闭包里的任务,同步执行。 + (RACScheduler *)mainThreadScheduler;:主线程中执行。 + (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority name:(nullable NSString *)name:异步线程,指定线程优先级和名字。 + (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority;:异步线程,指定线程优先级,名称默认。 + (RACScheduler *)scheduler;:异步线程,默认优先级和名称。 + (nullable RACScheduler *)currentScheduler;:当前线程。当在主线程或在-[RACScheduler schedule:]的block中才会执行。 eg:

    NSLog(@"-------------->currentThread:%@",[NSThread currentThread]);
    [[RACScheduler immediateScheduler] schedule:^{
        NSLog(@"-------------->immediateThread1:%@",[NSThread currentThread]);
    }];
    [[RACScheduler mainThreadScheduler] schedule:^{
        NSLog(@"-------------->mainThreadSchedulerThread2:%@",[NSThread currentThread]);
    }];
    [[RACScheduler schedulerWithPriority:RACSchedulerPriorityHigh] schedule:^{
        NSLog(@"-------------->PriorityThread3:%@",[NSThread currentThread]);
    }];

    [[RACScheduler scheduler] schedule:^{
        NSLog(@"-------------->schedulerThread4:%@",[NSThread currentThread]);
    }];
    [[RACScheduler currentScheduler] schedule:^{
        NSLog(@"-------------->currentSchedulerThread5:%@",[NSThread currentThread]);
    }];

打印:

2018-01-26 14:42:24.483686+0800 RACLoginDemo[75856:6897026] -------------->currentThread:<NSThread: 0x60000027ba80>{number = 4, name = (null)}
2018-01-26 14:42:24.484447+0800 RACLoginDemo[75856:6897026] -------------->immediateThread1:<NSThread: 0x60000027ba80>{number = 4, name = (null)}
2018-01-26 14:42:24.484919+0800 RACLoginDemo[75856:6897026] -------------->currentSchedulerThread5:<NSThread: 0x60000027ba80>{number = 4, name = (null)}
2018-01-26 14:42:24.484992+0800 RACLoginDemo[75856:6897022] -------------->PriorityThread3:<NSThread: 0x600000272140>{number = 3, name = (null)}
2018-01-26 14:42:24.485163+0800 RACLoginDemo[75856:6897038] -------------->schedulerThread4:<NSThread: 0x60000027bc00>{number = 5, name = (null)}
2018-01-26 14:42:24.489472+0800 RACLoginDemo[75856:6896563] -------------->mainThreadSchedulerThread2:<NSThread: 0x60000007cc40>{number = 1, name = main}

推荐阅读

细说ReactiveCocoa的冷信号与热信号

iOS函数响应式编程以及ReactiveCocoa的使用

Draveness’s Blog