RAC响应式编程---iOS笔记摘录

目录

前言

RAC(ReactiveCocoa) 
    函数式编程、响应式编程    
    Github上的开源框架

响应式编程,是一种通用的编程范式,提高了开发效率。
我的理解就是:监听事件,然后在事件发生后做相应回调处理。
/**
delegate、通知、block
KVO
*/
在命令式编程环境中,a=b+c表示将表达式的结果赋给a,而之后改变b或c的值不会影响a。
但在响应式编程中,可以做到a的值随b或c的更新而更新。
RAC框架图

1. 使用

引入(集成ReactiveCocoa框架)
    pod 'ReactiveObjC'
    #import <ReactiveObjC/ReactiveObjC.h>
  1. 常用
添加监听事件
    // 给UITextFiled添加值改变事件
    [[[UITextField new]rac_signalForControlEvents:UIControlEventEditingChanged]subscribeNext:^(id x) {
        //
    }];
    等同于(简化版)
    [[[UITextField new]rac_textSignal]subscribeNext:^(NSString * _Nullable x) {
        // UIControlEventEditingChanged
    }];

    // 给按钮添加点击事件
    [[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
添加手势
    [[UIButton new]addGestureRecognizer:({
        UITapGestureRecognizer *tapG=[UITapGestureRecognizer new];
        [[tapG rac_gestureSignal]subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
        }];
        tapG;
    })];
调用方法
    @weakify(self)
    [[self rac_signalForSelector:@selector(viewDidLoad)]subscribeNext:^(id x) {
        @strongify(self)
        //
    } error:^(NSError * _Nullable error) {
    } completed:^{
    }];
dele代理
    UIAlertView *alertV=[[UIAlertView alloc]initWithTitle:@"alert" message:@"content" delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"sure", nil];
    // 实现代理方法clickedButtonAtIndex 来自协议UIAlertViewDelegate
    [[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)]subscribeNext:^(id  _Nullable x) {
    }];
    // 等同于(简化版)
    [[alertV rac_buttonClickedSignal]subscribeNext:^(NSNumber * _Nullable x) {
    }];
    [alertV show];
NSNotificationCenter通知
    添加观察者
    [[[NSNotificationCenter defaultCenter]rac_addObserverForName:@"notiName" object:nil]subscribeNext:^(NSNotification * _Nullable noti) {
        // noti.name noti.object noti.object
    }];
    发送通知
    [[NSNotificationCenter defaultCenter]postNotificationName:@"notiName" object:@[@"1",@"2"]];
KVO键值观察
    // 方式一
    [RACObserve([UIScrollView new], contentOffset) subscribeNext:^(id  _Nullable x) {
    }];
    // 方式二
    [[self rac_valueForKeyPath:@"age" observer:nil] subscribeNext:^(id x ){
        NSLog(@"%@",x);
    }];
RACSubject
    // 用于view中不同button的点击事件
    RACSubject *subj=[RACSubject subject];
    [subj sendNext:@(100)];
    // 外部调用
    [subj subscribeNext:^(NSNumber *tag) {
    }];
    
cell.clickSubject 
    @weakify(self);
    [[cell.clickSubject takeUntil:cell.rac_prepareForReuseSignal] subscribeNext:^(NSNumber *x) {
        @strongify(self);
        [self clickCellBtn:[x intValue] atIndex:indexPath];
    }];
值绑定

RAC(self.label.text) = _textField.rac_textSignal;
RAC(_personVM,mobile)=_phoneTF.rac_textSignal;
  1. 接口命令(用于MVVM)
+(RACSignal *)PostWithURL:(NSString *)urlStr parameters:(NSDictionary *)paramDic animated:(BOOL)animated{
    return [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
            [YTNetworkTool POST:urlStr parameters:paramDic SuccessBlock:^(BOOL isOK, NSDictionary *dic) {
                // 回调
                [subscriber sendNext:dic];
                [subscriber sendCompleted];
            } failBlock:^(NSError *error) {
                [subscriber sendError:error];
            } animated:animated];
    
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"信号发送完成或发送错误后调用,会自动执行这个block,并取消订阅信号");
        }];
    }]replayLazily];
}
    _addRouteCommand=[[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        //
        NSDictionary *paramDic=@{@"isOpen":self.isOpen};
        return [YTServiceObject PostWithURL:API_AddRouteT parameters:paramDic animated:true];
    }];
    执行命令(调用接口)
    vm.isOpen=@(true);
    [[vm.addRouteCommand execute:nil] subscribeNext:^(NSDictionary *dic) { 
      // dic
    }];
    [[[vm.addRouteCommand execute:nil]deliverOnMainThread] subscribeNext:^(NSDictionary *dic) { 
      // dic
    }];

    // 命令在执行中(用于加载 加载中...)
    [[vm.addRouteCommand executing]subscribeNext:^(NSNumber * _Nullable x) {
        if(x.boolValue){
            // 加载 加载中...
        }else{
            // 隐藏 加载中...
        }
    }];

两个信号都收到时回调

RACSignal*signalA = [RACSignal createSignal:^RACDisposable *(id <RACSubscribe>subscriber){
  NSLog(@"数据请求1");
[subscriber sendNext:@"数据请求1请求下来的数据"];
return nil;
}];
RACSignal*signalB = [RACSignal createSignal:^RACDisposable *(id <RACSubscribe>subscriber){
  NSLog(@"数据请求2");
[subscriber sendNext:@"数据请求2请求下来的数据"];
return nil;
}];
[self rac_liftSelector:@Selector(updateUI) withSignalFromArray:@[signalA,signalB]];
 //将 textfield 输入信号的 返回值进行修改  得到新的信号!
    RACSignal *firstSignal = [self.firstTextfield.rac_textSignal map:^id(NSString *firstString) {
        if (firstString.length >= 5 && firstString.length <= 10) {
            return @(YES);
        }
        return @(NO);
    }];
     RACSignal *secondSignal = [self.secondTextfield.rac_textSignal map:^id(NSString *secondString) {
         if (secondString.length >5 && secondString.length < 10) {
             return @(YES);
         }
         return @(NO);
     }];
    // 绑定用户名、密码判断结果的2个信号量,如果都为真,则按钮可用
    RAC(self.loginButton,enabled) = [RACSignal combineLatest:@[firstSignal,secondSignal] reduce:^(NSNumber *firstRes,NSNumber *secondRes){
        return @(firstRes.boolValue && secondRes.boolValue);
    }];
  1. 信号加条件
map 映射(拦截做额外处理)

    [[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside]map:^id(id value) {
        NSLog(@"value: %@",value);
        return @"hello";
    }]subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
filter 过滤(拦截不符合情况)

    // 监听文本框的输入,而且只有大于3个长度的时候才会打印
    [[self.textField.rac_textSignal filter:^BOOL(id value) {
        return [value length] > 3;
    }]subscribeNext:^(id x) {
        NSLog(@"x:%@",x);
    }];
延时

[[RACScheduler mainThreadScheduler]afterDelay:2 schedule:^{
}];

[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]]subscribeNext:^(NSDate * date) {
}];

take:2   选取前两个信号
skip:2   跳过前两个信号
repeat   无限重复执行
delay:3  延迟3s发送信号
throttle:0.5           0.5s内信号不发生变化则触发
distinctUntilChanged   不会连续发送两次相同的信号
timeout:2       超时2s后触发error
ignore:@"1"     忽略信号1


[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
        [self.textField endEditing:YES];
        //        [alertView show];
      
        // 创建信号
        RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"1"];
            [subscriber sendNext:@"2"];
            [subscriber sendNext:@"3"];
            [subscriber sendNext:@"4"];
            [subscriber sendCompleted];
            return nil;
        }] take:2];  // skip:2、repeat
        
        // 发送信号
        [signal subscribeNext:^(id x) {
            NSLog(@"x : %@",x);
        } completed:^{
            NSLog(@"completed");
        }];
    }];
将多个不同类型的数据组合成一个元组
    // 把参数中的数据包装成元组
    RACTuple *tuple = RACTuplePack(@"xmg",@20,@"m",@(999),@[@"a"],@{@"key":@"value"});
    
    RACTupleUnpack(NSString *name,NSNumber *age,NSString *sex,NSNumber *price,NSArray *arr,NSDictionary *dic) = tuple;
    NSLog(@"name:%@  age:%@  sex:%@  price:%@ arr:%@  dic:%@",name,age,sex,price,arr,dic);

遍历数组、字典

    NSArray *contentArr=@[@"hello",@"world",@"!"];
    [contentArr.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
    }];

    NSDictionary *paramDic=@{@"hello":@"world"};
    [paramDic.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
        RACTwoTuple *tuple=(RACTwoTuple *)x;
        // key: tuple[0]
        // value: tuple[1]
    }];
  1. 基础
信号RACSignal
    // 1. 创建信号
    RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // 3. 发送信号
        [subscriber sendNext:@"唱歌"];
        // 调用该方法销毁信号,否则该信号一直占用着内存
        [subscriber sendCompleted];
        // 3.1 发送error信号
        [subscriber sendError:[NSError errorWithDomain:NSURLErrorDomain code:1001 userInfo:@{@"error":@"error message"}]];
        // 4. 销毁信号完毕后回调
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"singal已销毁");
        }];
    }];
    // 2. 订阅信号
    RACDisposable *disposable = [signalA subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];
    // 取消订阅
    // [disposable dispose];
    // 2.1 订阅error信号
    [signalA subscribeError:^(NSError * _Nullable error) {
        NSLog(@"%@",error);
    }];


信号动作:
  信号映射:map、flattenMap
  信号过滤:filter、ignore、distinctUntilChanged
  信号合并:combineLatest、reduce、merge、zipWith
  信号连接:concat、then
  信号操作时间:timeout、interval、dely
  信号跳过:skip
  信号取值:take、takeLast、takeUntil
  信号发送顺序:donext、cocompleted
  获取信号中的信号:switchToLatest
  信号错误重试:retry
避免循环引用
  @weakify(self)
  @strongify(self)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,188评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,464评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,562评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,893评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,917评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,708评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,430评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,342评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,801评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,976评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,115评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,804评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,458评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,008评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,135评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,365评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,055评论 2 355

推荐阅读更多精彩内容