ReactiveCocoa(其简称为 RAC)是由 Github 开源的一个应用于 iOS 和 OS X 开发的新框架。RAC 具有函数式编程和响应式编程的特性(FRP:函数响应式编程)。它主要吸取了 .Net 的 Reactive Extensions 的设计和实现。
资料:http://blog.devtang.com/2016/01/03/reactive-cocoa-discussion/
block用strong(非ARC用copy)
ReactiveCocoa(简称为RAC
),是由Github开源的一个应用于iOS和OS开发的新框架,Cocoa是苹果整套框架的简称,因此很多苹果框架喜欢以Cocoa结尾。
在我们iOS开发过程中,经常会响应某些事件来处理某些业务逻辑,例如按钮的点击,上下拉刷新,网络请求,属性的变化(通过KVO)或者用户位置的变化(通过CoreLocation)。但是这些事件都用不同的方式来处理,比如action、delegate、KVO、callback等。
其实这些事件,都可以通过RAC处理,ReactiveCocoa为事件提供了很多处理方法,而且利用RAC处理事件很方便,可以把要处理的事情,和监听的事情的代码放在一起,这样非常方便我们管理,就不需要跳到对应的方法里。非常符合我们开发中高聚合,低耦合
的思想。
在开发中我们也不能太依赖于某个框架,否则这个框架不更新了,导致项目后期没办法维护,比如之前Facebook提供的Three20框架
,在当时也是神器,但是后来不更新了,也就没什么人用了。因此我感觉学习一个框架,还是有必要了解它的编程思想
。
先简单介绍下目前咱们已知的编程思想
。
3.1 面向过程
:处理事情以过程为核心,一步一步的实现。
3.2 面向对象
:万物皆对象
3.3 链式编程思想
:是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好。a(1).b(2).c(3)
链式编程特点
:方法的返回值是block,block必须有返回值(本身对象),block参数(需要操作的值)
代表
:masonry框架。
练习一
:模仿masonry,写一个加法计算器,练习链式编程思想。
3.4 响应式编程思想
:不需要考虑调用顺序,只需要知道考虑结果,类似于蝴蝶效应,产生一个事件,会影响很多东西,这些事件像流一样的传播出去,然后影响结果,借用面向对象的一句话,万物皆是流。
代表
:KVO运用。
练习二
:KVO底层实现。
3.5 函数式编程思想
:是把操作尽量写成一系列嵌套的函数或者方法调用。
函数式编程本质
:就是往方法中传入Block,方法中嵌套Block调用,把代码聚合起来管理函数式编程特点
:每个方法必须有返回值(本身对象),把函数或者Block当做参数,block参数(需要操作的值)block返回值(操作结果)
代表
:ReactiveCocoa。
练习三
:用函数式编程实现,写一个加法计算器,并且加法计算器自带判断是否等于某个值.
ReactiveCocoa结合了几种编程风格:
函数式编程(Functional Programming)
响应式编程(Reactive Programming)
所以,你可能听说过ReactiveCocoa被描述为函数响应式编程(FRP)框架。
以后使用RAC解决问题,就不需要考虑调用顺序,直接考虑结果,把每一次操作都写成一系列嵌套的方法中,使代码高聚合,方便管理。
1 .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
2 .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
#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
@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
3 .响应式编程,如KVO,不需要考虑调用顺序,类似蝴蝶效应,产生一个事件,会自发地影响很多东西,像流一样的传播开去,然后影响结果.万物皆是流.
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.通知观察者,告诉你属性改变
// 就是去判断有没有调用一个对象的set方法
//
// Person.h
// 03KVO
//
// Created by beyond on 2017/12/10.
// Copyright © 2017年 beyond. All rights reserved.
//
#import
@interface Person : NSObject
{
// 只有指定为public之后,才可以直接使用->访问
@public int _age;
}
@property (nonatomic,assign) int age;
@end
//
// 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 *)change context:(void *)context
{
NSLog(@"sg__%d",_p.age);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 每点击次一屏幕,就调用set方法,改变age;
// _p.age ++;
_p->_age ++; // 不会触发,因为没有执行set方法
}
@end
4.仿造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 *)change context:(void *)context
{
NSLog(@"sg__%d",_p.age);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 每点击次一屏幕,就调用set方法,改变age;
_p.age ++;
// _p->_age ++; // 不会触发,因为没有执行set方法
}
@end
继承自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
@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
@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
@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
5.函数式编程:把操作尽量写成一系列嵌套的函数或方法调用.
特点:1.每个方法必须有返回值(本身对象),
2.把函数或者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
@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