RAC 基本操作

作者: 小苗晓雪 | 来源:发表于2016-12-18 14:20 被阅读283次

提示:

  • 1.请着重看代码块的内容,希望大家也能跟着我的代码敲一遍!大概了解一下 ReactiveCocoa 的代码风格~
  • 2.我要提示的是需要用 cocoapods 安装 RAC,这期间有很多坑,不建议用 Xcode 8版本,建议用 Xcode7版本跟敲代码!因为 RAC 新版本改版很多,我还没有来得及更新

主Bundle 栏

Snip20161218_6.png

文本框上的变化
动态的反映到调试去内:

模拟器Gif图演示.gif

Person.h 文件

#import <Foundation/Foundation.h>

@interface Person : NSObject

/**
 * 名字:
 */
@property (nonatomic, copy) NSString *name ;
/**
 * 年龄:
 */
@property (nonatomic, assign) NSInteger age ;

@end

** 模型 ---> UI 的绑定:把模型上的数据放到 UI 上显示:
关键语句:
RAC(TARGET, ...) = RACObserve(TARGET, KEYPATH) ; **

**UI --->模型的绑定:
核心语句:
[[RACSignal combineLatest:
@[nameTextField.rac_textSignal,
ageTextField.rac_textSignal]]
subscribeNext:
^(RACTuple *x) {
//first:name文本框里的内容 ;
_person.name = x.first ;
//second:age 文本框里的内容 ; }] ;
_person.age = [[x second] integerValue] ;
**

ViewController.m 文件

#import "ViewController.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
#import "Person.h"
/**
 *  int转 NSString 时候会用到这个宏定义!!!
 */
#define NSStringFromValue(value) [value description]

@interface ViewController ()

@property (nonatomic, weak) UIButton *button ;

@end

@implementation ViewController {
    Person *_person ;
}
/**
 *  MVVM设计模式:
    使用 ViewModel 的原因:
    1.可以把独立的业务逻辑代码(如:网络请求等)从控制器中抽取,便于测试 ;
    2.大型的架构级项目中 "MVC" 设计模式对控制器的依赖太严重!用 MVVM 可以简化 ViewController 的代码
 
    RAC 配合 MVVM:
    双向绑定
        3.1ReactiveCocoa 配合 MVVM 设计模式可以进行双向绑定(Model 的改变告诉 View,View 的改变也能告诉 Model)-->RAC 几乎是目前唯一的选择!
 */

- (void)viewDidLoad {
    [super viewDidLoad];
    //准备数据:
    _person = [[Person alloc] init] ;
    _person.name = @"zhangsan" ;
    _person.age = 18 ;
    [self dataBindEachOther] ;
    
//    [self buttonDemo] ;
//    [self textFieldCombineDemo1] ;
//    [self textFieldCombineDemo2] ;
}

#pragma mark - textFieldCombineDemo1 - 组合文本框1
- (void)textFieldCombineDemo1 {
    //passwordTextField
    UITextField *passwordTextField = [[UITextField alloc] initWithFrame:CGRectMake(20, 80, 300, 40)] ;
    passwordTextField.borderStyle = UITextBorderStyleRoundedRect ;
    [self.view addSubview:passwordTextField] ;
    //nameTextField
    UITextField *nameTextField = [[UITextField alloc] initWithFrame:CGRectMake(20, 40, 300, 40)] ;
    nameTextField.borderStyle = UITextBorderStyleRoundedRect ;
    [self.view addSubview:nameTextField] ;
    
    //监听文本输入目录:
    //订阅 subscribe 那个信号就可以监听那个信号!
    //passwordTextFieldSignal:
    RACSignal *passwordTextFieldSignal = [passwordTextField rac_textSignal] ;
    [passwordTextFieldSignal subscribeNext:^(id x) {
        NSLog(@"%@----%@" , x , [x class]) ;
    }] ;
    //nameTextFieldSignal:
    RACSignal *nameTextFieldSignal = [nameTextField rac_textSignal] ;
    [nameTextFieldSignal subscribeNext:^(id x) {
        NSLog(@"%@----%@" , x , [x class]) ;
    }] ;
    /**
     *  现在有两个信号:
        1.passwordTextFieldSignal
        2.nameTextFieldSignal
     *  在 RAC 中还有一个组合信号的概念~感觉这些和之前Apple 原生的一些代码比如多线程里的 "组队列" 的概念有些相似!我只要订阅这个组合信号就可以监听到这个组合信号里的所有的信号的变化,不用订阅组里的每一个信号一一订阅 ;
     */
    //combineLatest:组合信号:
    RACSignal *combineLatestSignal = [RACSignal combineLatest:@[passwordTextFieldSignal , nameTextFieldSignal]] ;
    [combineLatestSignal subscribeNext:^(RACTuple *x) {
        NSLog(@"combineLatest---%@----%@" , x , [x class]) ;
        NSString *name = x.first ;
        NSString *password = x.second ;
        NSLog(@"%@*******%@" , name , password) ;
    }] ;
    //RACTuple:RAC元组:(类似字典,可以包多个值) ;
}

#pragma mark - textFieldCombineDemo2 - 组合文本框2
/**
 *  系统提供的信号是始终存在的才能保证随时发生事件才能扑捉得到,因此,所有的 block 中,如果调用"self."或者"成员变量"点语法几乎肯定会出现循环引用!!!
 */
- (void)textFieldCombineDemo2 {
    //nameTextField
    UITextField *nameTextField = [[UITextField alloc] initWithFrame:CGRectMake(20, 40, 300, 40)] ;
    nameTextField.placeholder = @"nameTextField" ;
    nameTextField.borderStyle = UITextBorderStyleRoundedRect ;
    [self.view addSubview:nameTextField] ;
    RACSignal *nameTextFieldSignal = [nameTextField rac_textSignal] ;
    //passwordTextField
    UITextField *passwordTextField = [[UITextField alloc] initWithFrame:CGRectMake(20, 80, 300, 40)] ;
    passwordTextField.placeholder = @"passwordTextField" ;
    passwordTextField.borderStyle = UITextBorderStyleRoundedRect ;
    [self.view addSubview:passwordTextField] ;
    RACSignal *passwordTextFieldSignal = [passwordTextField rac_textSignal] ;
    /**
     *  现在有两个信号:
     1.passwordTextFieldSignal
     2.nameTextFieldSignal
     *  在 RAC 中还有一个组合信号的概念~感觉这些和之前Apple 原生的一些代码比如多线程里的 "组队列" 的概念有些相似!我只要订阅这个组合信号就可以监听到这个组合信号里的所有的信号的变化,不用订阅组里的每一个信号一一订阅 ;
     */
    //combineLatest:组合信号:
    //reduce:^id:返回值为 id 类型 ;
    //reduce: 减少 -->合并两个信号的数据进行汇总计算的时候使用:
    //在 reduce 中通过接受参数进行计算,并且返回需要的数值:\
    //for instance(例如): 只有用户名和密码同事存才的时候才允许登录(下一步操作)!
    RACSignal *combineLatestSignal = [RACSignal combineLatest:@[nameTextFieldSignal , passwordTextFieldSignal] reduce:^id (NSString *name , NSString *password) {
        NSLog(@"name = %@######password = %@" , name , password) ;
        //判断两者是否同时存在:
        return @(name.length > 0 && password.length > 0) ;
    }] ;
    //解除循环引用的方法1:
//    __weak typeof(self) weakSelf = self ;
    
    //⭐️解除循环引用的方法2-->RAC 特有的方法:
    //@weakify(self) 的后面可以不加分号!!!
    @weakify(self)
    //信号被订阅之后才是热信号!没有被订阅的信号称为"冷信号":
    [combineLatestSignal subscribeNext:^(id x) {
        //x = 0: 说明用户名和密码不同时存在, x = 1:用户名与密码同时存才:
        NSLog(@"combineLatest---%@----%@" , x , [x class]) ;
        //根据 x 的布尔值( boolValue )显示 button 与否:
        //weakSelf.button.enabled = [x boolValue] ;
        
        //⭐️解除循环引用的方法2-->RAC 特有的方法:
        //这里用@strongify(self)
        //相当于 weak - strong dance :强弱舞蹈:
        @strongify(self)
        self.button.enabled = [x boolValue] ;
    }] ;
}

#pragma mark - buttonDemo
- (void)buttonDemo {
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeContactAdd] ;
    btn.center = self.view.center ;
    [self.view addSubview:btn] ;
    //监听事件:不在需要新建一个方法!这种写法在现在的 iOS 还是比较常见的!
    //用的是在 block 里面写方法! bloc 可以把所有的相关代码都放在一起!
    RACSignal *signal = [btn rac_signalForControlEvents:UIControlEventTouchUpInside] ;
    [signal subscribeNext:^(id x) {
        NSLog(@"btn = %@" , x) ;
    }] ;
    _button = btn ;
}

#pragma mark - dealloc
//检测是否调用了 dealloc 方法:
- (void)dealloc {
    NSLog(@"%s" , __FUNCTION__) ;
}

#pragma mark - dataBindEachOther - 双向绑定
- (void)dataBindEachOther {
    UITextField *nameTextField = [[UITextField alloc] initWithFrame:CGRectMake(20, 60, 350, 50)] ;
    nameTextField.borderStyle = UITextBorderStyleRoundedRect ;
    [self.view addSubview:nameTextField] ;
    UITextField *ageTextField = [[UITextField alloc] initWithFrame:CGRectMake(20, 150, 350, 50)] ;
    ageTextField.borderStyle = UITextBorderStyleRoundedRect ;
    [self.view addSubview:ageTextField] ;
    
    /**
     *  双向绑定:--->
     **/
    
    /**
     *  模型 ---> UI 的绑定:把模型上的数据放到 UI 上显示:
        关键语句:RAC(TARGET, ...) = RACObserve(TARGET, KEYPATH) ;
     *
     */
    RAC(nameTextField , text) = RACObserve(_person, name) ;
    /**
     *  RAC(ageTextField , text)  = [RACObserve(_person, age) ;赋值类型不匹配! age 赋值给 text:
     *  崩溃提示:
        reason: '-[__NSCFNumber rangeOfCharacterFromSet:]: unrecognized selector sent to instance 0xb000000000000123'
     *  RAC 出现问题后调用堆栈 "深不见底" -->调试起来难度极大!
     */
    
    /**
     *  如果使用基本数据类型绑定 UI 的内容,需要使用 map 函数,通过 block 对 value 的数值进行转换之后才能够进行绑定!
     *
     */
    RAC(ageTextField , text)  = [RACObserve(_person, age) map:^id(id value) {
        //遇到不知道的这种 id 返回值时,想都不要想~就是 NSLog 打印一下看看:
        NSLog(@"%@^^^^^^^%@" , value , [value class]) ;
        /**
         *  开发中,经常会用到将int格式化成NSString,还在使用format(@"%d",int);那就过时了。
         #define NSStringFromValue(value) [value description]
         */
        return  NSStringFromValue(value) ;
    }] ;
    
    /**
     *  UI ---> 模型 的绑定:
        核心语句:
        [[RACSignal combineLatest:@[nameTextField.rac_textSignal , ageTextField.rac_textSignal]] subscribeNext:^(RACTuple *x) {
            _person.name = x.first ;//first:name文本框里的内容 ;
            _person.age = [[x second] integerValue] ;//second:age 文本框里的内容 ;
         }] ;
     */
    [[RACSignal combineLatest:@[nameTextField.rac_textSignal , ageTextField.rac_textSignal]] subscribeNext:^(RACTuple *x) {
        _person.name = x.first ;//first:name文本框里的内容 ;
        _person.age = [[x second] integerValue] ;//second:age 文本框里的内容 ;
    }] ;
    //添加按钮输出结果:
    UIButton *button = [UIButton buttonWithType:UIButtonTypeContactAdd] ;
    button.center = self.view.center ;
    [self.view addSubview:button] ;
    //RAC响应事件:
    [[button rac_signalForControlEvents: UIControlEventTouchUpInside] subscribeNext:^(id x) {
        //这里调用了 _person 这个成员变量,会造成循环引用:
        NSLog(@"%@-----%zd" , _person.name, _person.age) ;
    }] ;
}

@end

热爱分享,热爱开源

相关文章

  • RAC 基本操作

    提示: 1.请着重看代码块的内容,希望大家也能跟着我的代码敲一遍!大概了解一下 ReactiveCocoa 的代码...

  • ReactiveCocoa基本了解

    ReactiveCocoa的基本了解 ReactiveCocoa简称RAC。 RAC的基本结构 信号源* RACS...

  • RAC处理网络请求和界面交互

    前言 众所周知RAC学习曲线陡峭,作为新手的我也踩了不少坑.学习Rx的过程首先要学会基本的操作,然后再组合操作完成...

  • RxSwift中的组合操作

    今天谈一下RxSwift中的组合操作,和RAC一样。可以参照RAC理解。 Combination Operator...

  • RAC基本用法

    最近公司要用MVVM+RAC重构项目,所以就对RAC开发中的基本用法总结了一下。 RACSignal RACSig...

  • RAC基本用法

    1.信号的创建、订阅和发送 1.1创建信号 1.2 订阅信号 1.3 合并到一块的写法 2.RAC信号的监听 2....

  • ReactiveCocoa

    一、RAC的基本概念信号RACSignal: 信号是RAC中最核心的概念,贯穿在整个RAC框架中。它其实是一个信号...

  • Rac宏操作

    keypath 一个参数时, 取.后面的子字符串。如果没有.就取字符串本身二个参数时, 取的第二个参数的字符串。 ...

  • RAC高阶操作

    doNext、doError、doCompleted 在发送信号前做点什么?. throttle:和throttl...

  • RAC中常见的高级用法-bind方法(map和flattenMa

    RAC操作思想: Hook(钩子)思想 RAC核心方法:bind bind方法 假设想监听文本框的内容,并且在每次...

网友评论

    本文标题:RAC 基本操作

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