iOS_RAC_01_ReactiveCocoa框架介绍

常见编程思想介绍

在开发中我们也不能太依赖于某个框架,否则这个框架不更新了,导致项目后期没办法维护,比如之前Facebook提供的Three20框架,在当时也是神器,但是后来不更新了,也就没什么人用了。

因此学习一个框架,有必要了解其编程思想

先简单介绍下常见几种的编程思想

面向过程:处理事情以过程为核心,一步一步的实现。

面向对象:万物皆对象

链式编程思想:是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好。a(1).b(2).c(3)

  • 链式编程特点:方法的返回值是block,    block必须有返回值(对象本身),block参数(需要操作的值)
  • 代表masonry框架
  • 练习一:模仿masonry,写一个加法计算器,练习链式编程思想。

 

响应式编程思想:不需要考虑调用顺序,只需要知道考虑结果,类似于蝴蝶效应,产生一个事件,会影响很多东西,这些事件像流一样的传播出去,然后影响结果,借用面向对象的一句话,万物皆是流

  • 代表:KVO运用。
  • 练习二:KVO底层实现。

函数式编程思想:是把操作尽量写成一系列嵌套的函数或者方法调用。

  • 函数式编程本质:就是往方法中传入Block,方法中嵌套Block调用,把代码聚合起来管理
  • 函数式编程特点:每个方法必须有返回值(本身对象),把函数或者Block当做参数,block参数(需要操作的值)block返回值(操作结果)
  • 代表:ReactiveCocoa。
  • 练习三:用函数式编程实现,写一个加法计算器,并且加法计算器自带判断是否等于某个值.

RAC是什么?

RAC 全称: ReactiveCocoa , 是在 Github 上开源的一个应用于 iOS 和 OS X 开发的新框架。

RAC 具有函数式编程响应式编程的特性(FRP 即:  函数 响应式 编程)。

它主要吸取了 .Net 的 Reactive Extensions 的设计和实现。

Reactive Extensions(Rx)是一个类库,它集成了异步、基于可观察(observable)序列的事件驱动编程和LINQ-style的查询操作。使用Rx,开发人员可以用observable对象描述异步数据流,使用LINQ操作符异步查询数据和使用Schedulers控制异步过程中的并发。简而言之,Rx = Observables + LINQ + Schedulers。”

资料:http://blog.devtang.com/2016/01/03/reactive-cocoa-discussion/

block用strong(非ARC用copy)

 

ReactiveCocoa简介

ReactiveCocoa(简称为RAC),是由Github开源的一个应用于iOS和OS开发的新框架,Cocoa是苹果整套框架的简称,因此很多苹果框架喜欢以Cocoa结尾。


RAC优点

在我们iOS开发过程中,经常会响应某些事件来处理某些业务逻辑,

例如按钮的点击,上下拉刷新,网络请求,属性的变化(通过KVO)或者用户位置的变化(通过CoreLocation)。

但是这些事件都用不同的方式来处理,比如action、delegate、KVO、callback等。其实这些事件,都可以通过RAC处理,

ReactiveCocoa为事件提供了很多处理方法,而且利用RAC处理事件很方便,可以把要处理的事情,和监听的事情的代码放在一起,这样非常方便我们管理,就不需要跳到对应的方法里。

非常符合我们开发中高聚合,低耦合的思想。

 

ReactiveCocoa结合了几种编程风格:

函数式编程(Functional Programming)

响应式编程(Reactive Programming)

所以,你可能听说过ReactiveCocoa被描述为函数响应式编程(FRP)框架

以后使用RAC解决问题,就不需要考虑调用顺序,直接考虑结果,把每一次操作都写成一系列嵌套的方法中,使代码高聚合,方便管理。


在正式学习RAC编程之前,

先通过3个最简单的例子

直观感受一下几种编程思想不同之处:


链式编程示例:

Masonry的链式思想:

将多个操作(多行代码)通过点号.()链接在一起成为一句代码,代码的可读性好,如a(1).b(2).c(3)

 

链式编程特点:

方法的返回值是block, block必须有返回值(本身对象),block的参数(需要操作的数值)

//
//  ViewController.m
//  01mansonry
//
//  Created by beyond on 2017/12/8.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "ViewController.h"
// 拖入Masonry文件夹
#import "Masonry.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *redView = [[UIView alloc]init];
    redView.backgroundColor = [UIColor colorWithRed:arc4random_uniform(255)/255.0 green:arc4random_uniform(255)/255.0 blue:arc4random_uniform(255)/255.0 alpha:1];
    [self.view addSubview:redView];// 居中的长方形 
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.left.equalTo(@10);
        make.right.bottom.equalTo(@-10);
    }];
}




@end


Masonry的链式思想实现的计算器示例

//
//  ViewController.m
//  02Calculator
//
//  Created by beyond on 2017/12/10.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "ViewController.h"
#import "NSObject+Calculator.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 创建一个分类,使得任何类都可以用类方法,进行计算
    int result = [NSObject makeCalculator:^(CalculatorMaker *maker) {
        maker.add(1).add(20).multiply(3);
    }];
    NSLog(@"sg__%d",result);
}



@end


一个提供计算功能的分类:

//
//  NSObject+Calculator.h
//  02Calculator
//
//  Created by beyond on 2017/12/10.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "CalculatorMaker.h"
@interface NSObject (Calculator)
+(int)makeCalculator:(void (^)(CalculatorMaker *))block;
@end

//
//  NSObject+Calculator.m
//  02Calculator
//
//  Created by beyond on 2017/12/10.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "NSObject+Calculator.h"

@implementation NSObject (Calculator)
+(int)makeCalculator:(void (^)(CalculatorMaker *))block
{
    // 创建一个计算器
    CalculatorMaker *maker = [[CalculatorMaker alloc]init];
    // 将计算器传递给外部 控制器,并且计算器会在内部自动处理result
    block(maker);
    // 返回处理后的结果 
    return maker.result;
}
@end


一个计算器类:

//
//  CalculatorMaker.h
//  02Calculator
//
//  Created by beyond on 2017/12/10.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface CalculatorMaker : NSObject
@property (nonatomic,assign) int result;
// 加法:返回block (block的返回值是CalculatorMaker自身,参数是参与运算的值)
- (CalculatorMaker * (^)(int num))add;


// 乘法:返回block (block的返回值是CalculatorMaker自身,参数是参与运算的值)
- (CalculatorMaker * (^)(int num))multiply;
@end

//
//  CalculatorMaker.m
//  02Calculator
//
//  Created by beyond on 2017/12/10.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "CalculatorMaker.h"

@implementation CalculatorMaker
// 返回block (block的返回值是CalculatorMaker自身,参数是参与运算的值)
- (CalculatorMaker * (^)(int num))add
{
    return ^(int num){
        // 参与运算后,保存在成员变量_result内
        _result += num;
        return self;
    };
}



// 返回block (block的返回值是CalculatorMaker自身,参数是参与运算的值)
- (CalculatorMaker *(^)(int num))multiply
{
    return ^(int num){
        // 参与运算后,保存在成员变量_result内
        _result *= num;
        return self;
    };
}
@end


响应式编程示例:

响应式编程,如KVO,

不需要考虑调用顺序,类似蝴蝶效应,产生一个事件,会自发地影响很多东西,像流一样的传播开去,然后影响结果.

万物皆是流.


KVO底层实现:

1.动态创建新类:NSKVONotifying_Person,

NSKVONotifying_Person是Person子类,以作KVO用

2.修改当前对象p的isa指针->NSKVONotifying_Person

3.只要调用对象p的set,就会调用新类的NSKVONotifying_Person的set方法

4.重写新类NSKVONotifying_Person的set方法,

4.1.  调用[super set:]

4.2.  同时通知观察者,告诉你属性发生改变

// 就是去判断有没有调用一个对象的set方法


新建一个Person类

//
//  Person.h
//  03KVO
//
//  Created by beyond on 2017/12/10.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Person : NSObject
{
    // 只有指定为public之后,才可以直接使用->访问
    @public int _age;
}

@property (nonatomic,assign) int age;
@end


控制器中的代码:

使用系统提供的KVO

//
//  ViewController.m
//  03KVO
//
//  Created by beyond on 2017/12/10.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "ViewController.h"
// 触摸屏幕一下,年纪age++,通知观察者,在代理方法中打印变化的age
#import "Person.h"

@interface ViewController ()

@property (nonatomic,strong)Person *p;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    Person *p = [[Person alloc]init];
    _p = p;
    
    // 添加观察者
    [p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
    
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    NSLog(@"sg__%d",_p.age);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 每点击次一屏幕,就调用set方法,改变age;
    // _p.age ++;
    _p->_age ++; // 不会触发,因为没有执行set方法
}
@end


使用自己的KVO实现

//
//  ViewController.m
//  03KVO
//
//  Created by beyond on 2017/12/10.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "ViewController.h"
// 触摸屏幕一下,年纪age++,通知观察者,在代理方法中打印变化的age
#import "Person.h"
#import "NSObject+KVO.h"
@interface ViewController ()

@property (nonatomic,strong)Person *p;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    Person *p = [[Person alloc]init];
    _p = p;
    
    // 使用自己的KVO,添加观察者
    [p sg_addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
    
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    NSLog(@"sg__%d",_p.age);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 每点击次一屏幕,就调用set方法,改变age;
    _p.age ++;
    // _p->_age ++; // 不会触发,因为没有执行set方法
}
@end


自己仿造系统KVO实现原理, 自己定义一个类, 继承自Person

//
//  SGKVONotifying_Person.h
//  03KVO
//
//  Created by beyond on 2017/12/11.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "Person.h"

@interface SGKVONotifying_Person : Person

@end

//
//  SGKVONotifying_Person.m
//  03KVO
//
//  Created by beyond on 2017/12/11.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "SGKVONotifying_Person.h"
#import <objc/runtime.h>
@implementation SGKVONotifying_Person
// 自己实现KVO的底层
- (void)setAge:(int)age
{
    // 1.正常赋值
    [super setAge:age];
    
    // 2.当age值改变的时候,通过运行时获取前面保存的观察者
    id observer_ctrl = objc_getAssociatedObject(self, "observer_ctrl");
    // 3.调用观察者observer_ctrl的方法,告知age已改变
    [observer_ctrl observeValueForKeyPath:@"age" ofObject:self change:nil context:nil];
}
@end


创建一个NSObject的分类,自己手动实现addObserver方法

//
//  NSObject+KVO.h
//  03KVO
//
//  Created by beyond on 2017/12/11.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface NSObject (KVO)
// 添加分类,自己实现一个addObserver方法
- (void)sg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
@end

//
//  NSObject+KVO.m
//  03KVO
//
//  Created by beyond on 2017/12/11.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "NSObject+KVO.h"

// 修改isa指针时,保存观察者时用到运行时
#import <objc/runtime.h>

@implementation NSObject (KVO)
// 添加分类,自己实现一个addObserver方法
- (void)sg_addObserver:(NSObject *)observer_ctrl forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context
{
//    KVO底层实现:
//    1.动态创建新类:NSKVONotifying_Person,NSKVONotifying_Person是Person子类,以作KVO用
//    2.修改当前对象p的isa指针->NSKVONotifying_Person
//    3.只要调用对象p的set,就会调用新类的NSKVONotifying_Person的set方法
//    4.重写新类NSKVONotifying_Person的set方法,1.[super set:] 2.通知观察者,告诉你属性改变
    
    
    //1.改变isa指向自己实现KVO用到的的一个Person的子类
    object_setClass(self, NSClassFromString(@"SGKVONotifying_Person"));
    //2.给分类关联一个新的成员变量
    objc_setAssociatedObject(self, "observer_ctrl", observer_ctrl, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
}
@end


函数式编程示例:

函数式编程:把操作尽量写成一系列嵌套的函数或方法调用.

特点:

1.每个方法必须有返回值(本身对象),

2.把函数callbackFunction  或者  block  当成参数.

3.block的参数  是需要操作的变量

4.block的返回值  是操作完成后的结果


控制器中的代码:

//
//  ViewController.m
//  04函数式编程
//
//  Created by beyond on 2017/12/11.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "ViewController.h"
#import "Calculator.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    Calculator *calc = [[Calculator alloc]init];
    
    /*
     函数式编程:把操作尽量写成一系列嵌套的函数或方法调用.
     特点:1.每个方法(如下面的add方法)必须有返回值(本身对象),
     2.把函数或者block当成参数.
     3.block的参数是需要操作的成员变量
     4.block的返回值是操作完成后的结果
     */
    int output = [[calc add:^int(int result) {
        result += 1;
        result += 20;
        // 返回block操作完成后的值(目的是用于保存到成员变量里)
        return result;
    }] result];
    NSLog(@"sg_%d",output);
}




@end


计算器类代码:

//
//  Calculator.h
//  04函数式编程
//
//  Created by beyond on 2017/12/11.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Calculator : NSObject
@property (nonatomic,assign)int result;

// 返回值是本身
// 参数是一个block;
// block的参数是外部控制器要操作的成员变量result
// block的返回值是外部控制器操作完成后的结果数值
- (instancetype)add:(int (^)(int result))block;
@end

//
//  Calculator.m
//  04函数式编程
//
//  Created by beyond on 2017/12/11.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "Calculator.h"

@implementation Calculator
/*
 函数式编程:把操作尽量写成一系列嵌套的函数或方法调用.
 特点:1.每个方法(如下面的add方法)必须有返回值(本身对象),
 2.把函数或者block当成参数.
 3.block的参数是需要操作的成员变量
 4.block的返回值是操作完成后的结果
 */


// add方法:返回值是本身
// add方法:参数是一个block;
// block的参数是外部控制器要操作的成员变量result
// block的返回值是外部控制器操作完成后的结果数值
- (instancetype)add:(int (^)(int result))block
{
    //直接调用外部控制器的block,并且把外部控制器要操作的本类的成员变量传递进去,并且把block的返回值保存在成员变量里
    _result = block(_result);
    
    
    // 返回本身
    return self;
}
@end

 

未完待续,下一章节,つづく