美文网首页
ReactiveCocoa学习之RACSignal

ReactiveCocoa学习之RACSignal

作者: 最晴天 | 来源:发表于2018-05-23 17:24 被阅读12次

写这篇文章的目的,在于记录自己的学习内容,理顺思路。

RAC(Reactive Cocoa)

Cocoa是苹果整套框架的简称。Reactive响应。
第一次听说RAC,是有人问我,你知道函数响应式编程吗?
RAC就是。

那么,什么是函数响应式编程?
RAC让人觉得厉害的大概就是它的编程思想,事件的处理,经常会有一种让人忍不住赞叹一声,啊!或是一语惊醒梦中人的顿悟感。哈哈,我这个小白,在学习的过程中,是这样子没错啦。

函数式编程:

相关操作使用函数或者方法调用。
特点:每个方法都有返回值,并且使用block或函数作为参数。

顾名思义,采用函数的思想。
函数式编程,多采用闭包与高阶函数,函数是“第一等公民”,惰性计算(只用“表达式”,不用“语句”),递归(无“副作用”),不修改状态(引用透明性)。

例如,数学中常见的函数表达式
y = f(x) ——> y = f(f(x)) ,该表达式的参数是一个函数
应用到OC中,形式就大概如下

- (void)test:(void(^)(NSString *))block{
    
    block = ^(NSString *name){
//表达式
    };
}

响应式编程:

这种于操作结果,而不在意该结果究竟是按照怎样的操作顺序发生变化导致的。有点类似于蝴蝶效应,a发生变化,b自然跟随发生变化。
例如:

int a = 10;
int b = a+10;

我们希望a的值改变时,b的值随之改变,这就是响应,b自然响应a

具体,推荐阅读袁峥大神的https://www.jianshu.com/p/87ef6720a096
几种常见的编程思想介绍具体,而且还有使用例子。

RAC类结构图.jpg

RACSignal

RAC的使用过程,不外乎于
1.信号的产生 2.订阅信号 3.发送信号 4.信号销毁。

//1:信号的产生
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        // block调用时刻:每当有订阅者订阅信号,就会调用block。
        //3:发送信号
        [subscriber sendNext:@"龙晨"];

        // 如果不在发送数据,最好发送信号完成,内部会自动调用[RACDisposable disposable]取消订阅信号。

        NSError *error = [[NSError alloc] initWithDomain:NSURLErrorDomain code:1008611 userInfo:@{@"key":@"我的错"}];
        [subscriber sendError:error];
        
        //4:信号销毁
        //RACDisposable 信号回收站
        return [RACDisposable disposableWithBlock:^{
 // block调用时刻:当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号。
            
            // 执行完Block后,当前信号就不在被订阅了。
            NSLog(@"信号销毁了");
        }];
    }];
    
    //2:订阅信号
    [signal subscribeNext:^(id  _Nullable x) {
// block调用时刻:每当有信号发出数据,就会调用block.
        NSLog(@"%@",x);
    }];
    
    [signal subscribeError:^(NSError * _Nullable error) {
        NSLog(@"%@",error);
    }];

代码自上而下调用,那么3和4的发生,为什么会在2之后呢?明明3和4的代码在前呀?
为什么呢?

RACSignal,信号类,默认是冷信号,只有订阅了这个信号,才会变为热信号,值发生变化时触发。
订阅信号,调用RACSignal的subscribeNext方法。

1.创建信号,调用的方法

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    return [RACDynamicSignal createSignal:didSubscribe];
}

我们注意到,该方法的参数为didSubscribe的一个block,返回值是一个RACDynamicSignal类。

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    RACDynamicSignal *signal = [[self alloc] init];
    signal->_didSubscribe = [didSubscribe copy];
    return [signal setNameWithFormat:@"+createSignal:"];
}

在该方法中,didSubscribe被保存起来,此时并不会触发。

2.当信号被订阅时,在创建RACSubscriber的同时,nextBlock也被保存起来

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    NSCParameterAssert(nextBlock != NULL);
    
    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    return [self subscribe:o];
}
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCParameterAssert(subscriber != nil);

    RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
    subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];

    if (self.didSubscribe != NULL) {
        RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
            RACDisposable *innerDisposable = self.didSubscribe(subscriber);
            [disposable addDisposable:innerDisposable];
        }];

        [disposable addDisposable:schedulingDisposable];
    }
    
    return disposable;
}

+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
    RACSubscriber *subscriber = [[self alloc] init];

    subscriber->_next = [next copy];
    subscriber->_error = [error copy];
    subscriber->_completed = [completed copy];

    return subscriber;
}

- (instancetype)init {
    self = [super init];

    @unsafeify(self);

    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;
}

同时还创建了RACCompoundDisposable与RACPassthroughSubscriber(真正的订阅者类),同时RACDynamicSignal的didSubscribe被调用。
而在didSubscribe中,会调用[subscriber sendNext:];

#pragma mark RACSubscriber

- (void)sendNext:(id)value {
    if (self.disposable.disposed) return;

    if (RACSIGNAL_NEXT_ENABLED()) {
        RACSIGNAL_NEXT(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString([value description]));
    }

    [self.innerSubscriber sendNext:value];
}

- (void)sendNext:(id)value {
    @synchronized (self) {
        void (^nextBlock)(id) = [self.next copy];
        if (nextBlock == nil) return;

        nextBlock(value);
    }
}

sendNext底层其实就是执行subscriber的nextBlock

RACSignal可用于textFiled,button,手势,通知,KVO,定时器等。

 @weakify(self);
    //textField
    [self.accountTF.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
            @strongify(self);
        self.imgV.image = [UIImage imageNamed:x];
        if (self.imgV.image == nil) {
            self.imgV.image = [UIImage imageNamed:@"touxiang"];
        }
    }];
    
    //button
    [[self.loginBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
            @strongify(self);
        
    }];
    
    
    //定时器
    [[RACSignal interval:1 onScheduler:[RACScheduler scheduler]] subscribeNext:^(NSDate * _Nullable x) {
        NSLog(@"%@",x);
    }];
    
    //手势
    UITapGestureRecognizer *tap = [UITapGestureRecognizer new];
    self.stateLabel.userInteractionEnabled = YES;
    [self.stateLabel addGestureRecognizer:tap];
    [[tap rac_gestureSignal] subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
        NSLog(@"%@",x);
    }];
    
    //通知
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardDidChangeFrameNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
        NSLog(@"%@",x);
    }];
    
    //KVO
    [RACObserve(self, name) subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];

RAC中常见的相关操作:

1.信号映射:map与flattenMap
2.信号过滤:filter、ignore、 distinctUntilChanged
3.信号合并: combineLatest、reduce、merge、zipWith
4.信号连接:concat、then
5.信号操作时间:timeout、interval、dely
6.信号取值:take、takeLast、takeUntil、
7.信号跳过:skip
8.信号发送顺序:donext、cocompleted
9.获取信号中的信号:switchToLatest
10.信号错误重试:retry

简单使用举例:

//map  flattenMap
    //实际应用时 通常会跳过第一次
    [[[self.accountTF.rac_textSignal skip:1] flattenMap:^__kindof RACSignal * _Nullable(NSString * _Nullable value) {
        
        return [RACSignal return:[NSString stringWithFormat:@"+86-%@",value]];
        
    }]  subscribeNext:^(id  _Nullable x) {
        NSLog(@"flattenMap  ===  %@",x);
    }];
    
    
    //filter
    [[[self.passwordTF.rac_textSignal skip:1] filter:^BOOL(NSString * _Nullable value) {
        //做过滤条件
        @strongify(self);
        if (self.passwordTF.text.length > 5) {
            self.passwordTF.text = [self.passwordTF.text substringToIndex:5];
            self.stateLabel.text = @"密码长度不能大于5";
        }
        return value.length < 5;
        
    }] subscribeNext:^(NSString * _Nullable x) {
      NSLog(@"我订阅到了什么:%@",x);
        //逻辑区域
    }] ;

    
    //组合 combineLaster
    RACSignal *signalA = self.accountTF.rac_textSignal;
    RACSignal *signalB = self.passwordTF.rac_textSignal;
    
    [[RACSignal combineLatest:@[signalA, signalB] reduce:^id (NSString *account,NSString *password){
        //reduceBlock的参数个数 要与 合并的信号数组的个数保持一致
        //此处 可以将多个参数合并
        return @(account.length && password.length);
    }] subscribeNext:^(NSNumber *x) {
        self.loginBtn.backgroundColor = x.integerValue ? [UIColor greenColor] : [UIColor darkGrayColor];
        self.loginBtn.enabled = x.integerValue;
    }];
    
    //takeUntil
    //subject1 正常发送,直到subject2发送信号。意味着subject2发送信号后 subject1不能再发送信号
    //以下代码,reboot不能正常发送,除非注释掉 [subject2 sendNext:@"hani"];
    
    RACSubject *subject1 = [RACSubject subject];
    RACSubject *subject2 = [RACSubject subject];
    
    [[subject1 takeUntil:subject2] subscribeNext:^(id  _Nullable x) {
        
        NSLog(@"%@",x);
    }];
    
    [subject1 sendNext:@"菲尼克斯"];
    [subject1 sendNext:@"艾拇"];
    
    [subject2 sendNext:@"hani"];
    
    [subject1 sendNext:@"reboot"];

说明:

(a)flattenMap:在bind基础上封装的改变 法, 提 供的block,改变当前流,变成block返回的流对象。
(b)flatten:在flattenMap基础封装的改变 法,如果当前 反应流中的对象也是 个流的话,就可以将当前流变成当前
流中的流对象
(c)map:在flattenMap基础上封装的改变 法,在 flattenMap中的block中返回的值必须也是流对象, map则
需要,它是将流中的对象执 block后, 流的return 法 将值变成流对象。
(d)mapReplace:在map的基础上封装的改变 法,直接 替换当前流中的对象,形成 个新的对象流。
(e)filter:在Map基础上封装的改变封装,过滤掉当前流 中 符合要求的对象,将之变为空流
(f)ignore:在filter基础封装的改变 法,忽 和当 前值 样的对象,将之变为空流
(g)skip:在bind基础上封装的改变 法,忽 当前 流前n次的对象值,将之变为空流
(h)take:在bind基础上封装的改变 法,只区当 前流中的前n次对象值,之后将流变为空( 是空
流)。
(i)distinctUntilChanged:在bind基础封装的改变 法,当流中后 次的值和前 次的值 同的时候,
才会返回当前值的流,否则返回空流(第 次默认被 忽 )
(j)takeUntilBlock:在bind基础封装的改变 法,取当前 流的对象值,直到当前值满 提供的block,就会将当前流
变为空( 是空流)
(k)takeWhileBlock:在bind基础封装的改变 法,取当
前流的对象值,直到当前值 满 提供的block,就会将当 前流变为空( 是空流)
(l)skipUntilBlock:在bind基础封装的改变 法,忽 当 前流的对象值(变为空流),直到当前值满 提供的
block。
(m)skipWhileBlock:在bind基础封装的改变 法,忽 当前流的对象值(变为空流),直到当前值 满 提供的 block
(n)scanWithStart: reduceWithIndex:在bind基
础封装的改变 法, 同样的block执 每次流中的
值,并将结果 于后 次执 当中,每次都把block 执 后的值变成新的流中的对象。
(o)startWIth:在contact基础上封装的多流之间 的顺序 法,在当前流的值流出之前,加 个初
始值 (p)zip:打包多流,将多个流中的值包装成 个
RACTuple对象
(q)reduceEach:将流中的RACTuple对象进 过 滤,返回特定的衍 出的 个值对象

相关文章

网友评论

      本文标题:ReactiveCocoa学习之RACSignal

      本文链接:https://www.haomeiwen.com/subject/scwrjftx.html