美文网首页
RAC-响应式编程

RAC-响应式编程

作者: York_Lee | 来源:发表于2017-09-07 11:15 被阅读0次


    什么是RAC:

    git 重量型开源项目,主要是针对各种事件的处理 。

    什么是响应式编程:

    一个简单的理解:如果a + b =c 修改 a 在 修改 b c会变吗?结果是不会变的 ,如果c 变是需要再次调用 a + b = c 这个计算,响应式编程:就是修改a 或者 b 的时候 c就会 立即变换。所以说:响应式编程就是 在事件发生变换的时候立即做出相应.


    IOS 开发中有哪些事件发生:

    tagrget

    delegate

    kvo

    通知

    时钟(NSTime)

    网络异步回调


    RAC家族:4大家族

    备注:(如果只使用 请使用 3.0.0以下并且 指点家族 )

    pod search ReactiveObjC


    RACSignal:

    具体使用: 简称《信号3部曲》

    /*

    信号:

    1:创建信号 :(冷信号)

    2:订阅信号:(热信号)

    3:发送信号:

    */

    //    创建信号(冷信号)

    RACSignal *signal =  [RACSignal createSignal:^RACDisposable *(id subscriber) {

    //        发送信号

    [subscriber sendNext:@"this is signal"];

    return nil;

    }];

    //    订阅信号  (subscribe) 订阅(热信号)

    [signal subscribeNext:^(id x) {

    //      x :信号内容

    NSLog(@"x is value :%@ ",x);

    }];


    构造方法分析

    + (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe {

    return [RACDynamicSignal createSignal:didSubscribe];

    }

    RACDynamicSignal:动态信号

    看看这个方法:

    [RACDynamicSignal createSignal:didSubscribe]

    + (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe {

    RACDynamicSignal *signal = [[self alloc] init];

    signal->_didSubscribe = [didSubscribe copy];

    return [signal setNameWithFormat:@"+createSignal:"];

    }

    结论:创建信号的时候干了2件事情

    1:创建了RACDynamicSignal

    2:保存一个block didSubscribe


    创建信号的内部处理:

    //    创建冷信号

    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id  _Nonnull subscriber) {

    NSLog(@"创建信号");

    [subscriber sendNext:@"发送信号已经收到"];

    NSLog(@"发送信号");

    return nil;

    }];

    //    订阅信号 (热信号)

    [signal subscribeNext:^(id  _Nullable x) {

    //        x:信号内容

    NSLog(@"this is %@",x);

    NSLog(@"我订阅了信号");

    }];

    1:测试: 将订阅信号注释掉 查看日志 结果是什么都没有 so 假设结论:创建信号必须先订阅

    2:测试:将发送信号代码去掉:结果 :打印出创建信号log so 假设结论:我要订阅信号必须先发送


    订阅信号的内部处理

    //    订阅信号 (热信号)

    [signal subscribeNext:^(id  _Nullable x) {

    //        x:信号内容

    NSLog(@"this is %@",x);

    NSLog(@"我订阅了信号");

    }];

    进入 subscribeNext :

    - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {

    NSCParameterAssert(nextBlock != NULL);

    RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];

    return [self subscribe:o];

    }

    *记录下nextBlock

    在进入:subscriberWithNext: 发现创建了RACSubscriber 点击进入 subscribe:选择 RACDynamicSignal 的方法,发现 在subscribe方法中执行了,self.didSubscribe(subscriber) 这里解释了:创建信号必须先订阅(不订阅 block 就不执行)

    如果 我要订阅信号必须先发送

    - (void)sendNext:(id)value {

    @synchronized (self) {

    void (^nextBlock)(id) = [self.next copy];

    if (nextBlock == nil) return;

    nextBlock(value);

    }

    }

    执行nextBlock


    具体的流程如下:

    RAC 的简单使用


    KVO的简单使用

    kvo 的使用实现对_p对象的 name 属性监听

    //    kvo

    _p = [[Person alloc]init];

    //    对P监听

    [RACObserve(self.p, name)subscribeNext:^(id  _Nullable x) {

    NSLog(@"x is value  %@",x);

    }];

    点击屏幕修改p的name值

    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

    self.p.name = [NSString stringWithFormat:@"mrlee %05d",arc4random_uniform(200)];

    }


    target使用

    一句话搞定 创建 订阅 发送 三部曲

    [[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {

    NSLog(@"x is value %@",x);

    }];

    我们点击按钮显示x的值为:

    x is value button

    拿个这个是不是想干啥 就干啥~


    输入框点击监听

    [[self.textField rac_textSignal]subscribeNext:^(NSString * _Nullable x) {

    NSLog(@"x is value %@",x);

    }


    通知

    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil]subscribeNext:^(NSNotification * _Nullable x) {

    NSLog(@"ios is value %@",x);

    }];


    RAC中的坑

    循环引用

    1.制作循环引用案例

    首先 做a push b 页面 在b页面添加如下代码

    -(void)dealloc{

    NSLog(@"bay bay !");

    }

    在从b pop 回 a 打印出bay bay ! 说明VC 释放

    修改下

    - (void)viewDidLoad {

    [super viewDidLoad];

    [self demo2];

    }

    -(void)demo2{

    //    信号的生成

    [[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {

    NSLog(@"x is value %@",x);

    self.textField.text =@"你好~";

    }];

    }

    点击按钮 在pop 回 a 没有走bay bay ! 说明循环引用

    修改下

    //    信号的生成

    __weak typeof (self) weakSelf = self;

    [[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {

    NSLog(@"x is value %@",x);

    weakSelf.textField.text =@"你好~";

    }];

    打印bay bay

    RAC 提供了相关解决方案 @weakify(self);  @strongify(self);

    //    信号的生成

    @weakify(self);

    [[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {

    @strongify(self);

    NSLog(@"x is value %@",x);

    self.textField.text =@"你好~";

    }];


    是如何造成循环引用的

    self.btn 对 self.view.subview 进行强引用 self.textField 是self.btn 对其强引用 因为 他是在self.btn的Block中 self.textField 又被self.view.subview 进行强引用 so 循环引用

    这个说明:只要在rac中用了self 就会造成循环引用,解决办法 @weakify 和 @strongify


    Command命令

    //    创建命令

    RACCommand *command = [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {

    return nil;

    }];

    //    执行命令

    [command execute:@"xx"];

    运行这段代码 会发现 app carsh 掉了 原因是因为 return nil; 应该返回一个信号

    修改后

    //    创建命令

    RACCommand *command = [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {

    return [RACSignal createSignal:^RACDisposable * _Nullable(id  _Nonnull subscriber) {

    return nil;

    }];

    }];

    //    执行命令

    [command execute:@"xx"];

    input:执行命令的内容 (输入的指令)这样就不会崩溃了但是一个流程还没有走完 还差个接收 一个完整的流程如下

    //    创建命令

    RACCommand *command = [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {

    //        input 执行命令里的内容

    return [RACSignal createSignal:^RACDisposable * _Nullable(id  _Nonnull subscriber) {

    [subscriber sendNext:@"我是从命令里面发送的消息"];

    return nil;

    }];

    }];

    //    执行命令

    [[command execute:@"xx"] subscribeNext:^(id  _Nullable x) {

    NSLog(@"x is value %@",x);

    }];


    MVVM

    具体是什么是mvvm 我想不用说也有很多的资料讲解 。这里直接就不多说了,只说如下几点

    1:MVVM :就是MVC的瘦身剂

    2:MVVM 方便功能测试

    下面做个简单的登录页面 :需求 如果没有输入 userName 或者pwd 登录按钮是无法点击

    具体实现如下:

    //    绑定

    //    多个信号绑定成一个信号

    [[RACSignal combineLatest:@[self.userName.rac_textSignal,self.passWord.rac_textSignal] reduce:^id _Nullable(NSString *userName,NSString *pwd){

    return @(userName.length && pwd.length);

    }]subscribeNext:^(id  _Nullable x) {

    NSLog(@"x is value %@",x);

    }];

    1使用:combineLatest reduce 进行多个信号的绑定

    // RAC(self.loginBtn,enabled) 监听 UI 的状态  整合后

    RAC(self.loginBtn,enabled) =  [RACSignal combineLatest:@[self.userName.rac_textSignal,self.passWord.rac_textSignal] reduce:^id _Nullable(NSString *userName,NSString *pwd){

    return @(userName.length && pwd.length);

    }];

    OK 搞定 么又看出就这一句代码搞定 判断 username 和 pwd都必须都有值 才可以点点击按钮

    RAC(target,...) 用于监听 一个对象的相关属性 返回的为 RACSignal

    combineLatest 绑定多个有共性的信号

    demo地址如果对您有用请点个star

    相关文章

      网友评论

          本文标题:RAC-响应式编程

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