一.编程思想
先简单介绍下目前咱们已知的编程思想。
1.面向过程:处理事情以过程为核心,一步一步的实现。
C语言是面向过程编程的.
面向过程编程主要使用顺序、条件选择、循环三种基本结构来编写程序。
1.1 顺序:按照时间轴顺序完成每个处理;
1.2 条件选择:根据条件的成立与否执行不同的条件分支;
1.3 循环:根据一定的条件反复执行同样的代码;面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
例如:洗衣机的洗衣功能:放水、洗涤、脱水、打干,按照一定的顺序执行功能:放水->洗涤->脱水->放水->洗涤->脱水->打干。
在早期计算机配置低、内存小为了节省内存空间,大都采用面向过程编程(以时间换空间)。
2.面向对象:万物皆对象
满足面向对象编程的语言,一般会提供类、封装、继承、多态等语法和概念来辅助我们进行面向对象编程。
所谓的面向对象就是将我们的程序模块化、对象化,把具体事物的特性属性和通过这些属性来实现一些动作的具体方法封装到一个类里面,然后创建实例对象,以对象作为程序的基本模块来进行软件的分析、设计和开发的一种思考方法。
3.链式编程思想:是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好。a(1).b(2).c(3)
链式编程特点:方法的返回值是block,block必须有返回值(本身对象),block参数(需要操作的值)
代表:masonry框架。
例子:
TestView.h
使用方法:
4.响应式编程思想:不需要考虑调用顺序,只需要知道考虑结果,类似于蝴蝶效应,产生一个事件,会影响很多东西,这些事件像流一样的传播出去,然后影响结果,借用面向对象的一句话,万物皆是流。
代表:KVO运用。4.1 响应式编程(FRP Functional Reactive Programming),为解决现代编程问题提供了全新的视角.一旦理解它,可以极大地简化你的项目,特别是处理嵌套回调的异步事件,复杂的列表过滤和变换,或者时间相关问题。
4.2 响应式编程是一种新的编程风格,其特点是异步或并发、事件驱动、推送PUSH机制以及观察者模式的衍生。reactive 应用(响应式应用)允许开发人员构建事件驱动(event-driven),可扩展性,弹性的反应系统:提供高度敏感的实时的用户体验感觉,可伸缩性和弹性的应用程序栈的支持,随时可以部署在多核和云计算架构。
二.ReactiveCocoa初见
简介
ReactiveCocoa简称RAC,是一个iOS和OS中的函数式响应式编程框架。
作用
RAC关键解决的问题是开发中经常回见的“低聚合,高耦合”问题。在RAC出现之前,我们编写iOS代码,大部分都是在响应一些事件:按钮点击、接收网络消息、属性变化等等。但处理事件的形式在苹果官方API中却有好几种:如target-action、代理方法、KVO、回调或其它。以上这几种,往往在一个项目中基本都会使用到,在不同的地方会出现很多处理事件的形式,这就带来了不能很好统一管理问题。因此,我们想,有没有一个统一管理的解决方案呢?这个方案又是怎样的呢?到这里ReactiveCocoa就该粉墨登场了,它出现的目的就是为了解决统一标准去管理代码中的事件。
思想
1.函数式编程(Functional Programming):使用高阶函数,例如函数用其他函数作为参数。
2.响应式编程(Reactive Programming):关注于数据流和变化传播。所以,你可能听说过ReactiveCocoa被描述为函数响应式编程(FRP)框架。 以后使用RAC解决问题,就不需要考虑调用顺序,直接考虑结果,把每一次操作都写成一系列嵌套的方法中,使代码高聚合,方便管理。
原理
1、运用的是Hook(钩子)思想,Hook是一种用于改变API(应用程序编程接口:方法)执行结果的技术.
2、Hook用处:截获API调用的技术。
3、Hook原理:在每次调用一个API返回结果之前,先执行你自己的方法,改变结果的输出。
三.ReactiveCocoa 在objective-c下的简单使用
目前最新的版本是Swift的 如果想在OC环境下使用只能用 2.5 的版本
Pod 导入
pod 'ReactiveCocoa', '2.5'
1.RACSiganl
RACSiganl:信号类,一般表示将来有数据传递,只要有数据改变,信号内部接收到数据,就会马上发出数据。
信号类(RACSiganl),只是表示当数据改变时,信号内部会发出数据,它本身不具备发送信号的能力,而是交给内部一个订阅者去发出。
默认一个信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发。
如何订阅信号:调用信号RACSignal的subscribeNext就能订阅。
// RACSignal使用步骤:
// 1.创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3.发送信号 - (void)sendNext:(id)value
[subscriber sendNext:@"12345"];
// 如果不在发送数据,最好发送信号完成,内部会自动调用[RACDisposable disposable]取消订阅信号。
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
// block调用时刻:当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号。
// 执行完Block后,当前信号就不在被订阅了。
NSLog(@"signal 销毁");
}];
}];
// 2.订阅信号,才会激活信号. - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
[signal subscribeNext:^(id x) {
// block调用时刻:每当有信号发出数据,就会调用block.
NSLog(@"接收到消息->%@", x);
}];
2.RACSubject
RACSubject:信号提供者,自己可以充当信号,又能发送信号。
使用场景:通常用来代替代理,有了它,就不必要定义代理了。
// RACSubject使用步骤
// 1.创建信号 [RACSubject subject],跟RACSiganl不一样,创建信号时没有block。
RACSubject *subject = [RACSubject subject];
// 2.订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
[subject subscribeNext:^(id x) {
// block调用时刻:当信号发出新值,就会调用.
NSLog(@"第一个订阅者%@",x);
}];
[subject subscribeNext:^(id x) {
// block调用时刻:当信号发出新值,就会调用.
NSLog(@"第二个订阅者%@",x);
}];
// 3.发送信号 sendNext:(id)value
[subject sendNext:@"逗比123"];
3.RACReplaySubject
RACReplaySubject:重复提供信号类,RACSubject的子类。
使用场景一: 如果一个信号每被订阅一次,就需要把之前的值重复发送一遍,使用重复提供信号类。
使用场景二: 可以设置capacity数量来限制缓存的value的数量,即只缓充最新的几个值。
// RACReplaySubject使用步骤:
// 1.创建信号 [RACSubject subject],跟RACSiganl不一样,创建信号时没有block。
RACReplaySubject *replaySubject = [RACReplaySubject subject];
// 2.可以先订阅信号,也可以先发送信号。
//发送信号
[replaySubject sendNext:@"订阅之前发送的1"];
// 2.1 订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
[replaySubject subscribeNext:^(id x) {
NSLog(@"第一个订阅者接收到的数据%@",x);
}];
// 2.2 发送信号 sendNext:(id)value
[replaySubject sendNext:@"订阅之后发送的2"];
4.RACTuple RACSequence
RACTuple 元组类,类似NSArray,用来包装值.
RACSequence:RAC中的集合类,用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典。
/**
RACTuple:元组类,类似NSArray,用来包装值.
*/
/**
RACSequence:RAC中的集合类,用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典。
*/
// 1.遍历数组
NSArray *strArr = @[@"1", @"2", @"3", @"4", @"5", @"6",@"1", @"2", @"3", @"4", @"5", @"6"];
// 这里其实是三步
// 第一步: 把数组转换成集合RACSequence numbers.rac_sequence
// 第二步: 把集合RACSequence转换RACSignal信号类,numbers.rac_sequence.signal
// 第三步: 订阅信号,激活信号,会自动把集合中的所有值,遍历出来。
[strArr.rac_sequence.signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 2.遍历字典,遍历出来的键值对会包装成RACTuple(元组对象)
NSDictionary *dict = @{@"name":@"xmg",@"age":@18};
[dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
// 解包元组,会把元组的值,按顺序给参数里面的变量赋值
RACTupleUnpack(NSString *key, NSString *value) = x;
// 相当于以下写法
// NSString *key = x[0];
// NSString *value = x[1];
NSLog(@"%@ %@",key,value);
}];
// 3.字典转模型
NSMutableArray *dictArr = [NSMutableArray array];
for (int i=0; i<10; i++) {
[dictArr addObject:@{@"name":[NSString stringWithFormat:@"张%@", @(i)], @"age":@(i)}];
}
NSArray *objArr = [[dictArr.rac_sequence.signal map:^id(id value) {
return [Person personWithDict:value];
}] toArray];
NSLog(@"->%@", objArr);
5.RACCommand
RAC中用于处理事件的类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中,他可以很方便的监控事件的执行过程。
使用场景:监听按钮点击,网络请求
// 1.创建命令 initWithSignalBlock:(RACSignal * (^)(id input))signalBlock
// 2.在signalBlock中,创建RACSignal,并且作为signalBlock的返回值
// 3.执行命令 - (RACSignal *)execute:(id)input
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
NSLog(@"执行命令");
// 创建空信号,必须返回信号
// return [RACSignal empty];
// 2.创建信号,用来传递数据
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"请求数据789"];
// 注意:数据传递完,最好调用sendCompleted,这时命令才执行完毕。
[subscriber sendCompleted];
return nil;
}];
}];
// 强引用命令,不要被销毁,否则接收不到数据
_conmmand = command;
// 3.订阅RACCommand中的信号
[command.executionSignals subscribeNext:^(id x) {
[x subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}];
// RAC高级用法
// switchToLatest:用于signal of signals,获取signal of signals发出的最新信号,也就是可以直接拿到RACCommand中的信号
[command.executionSignals.switchToLatest subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 4.监听命令是否执行完毕,默认会来一次,可以直接跳过,skip表示跳过第一次信号。
[[command.executing skip:1] subscribeNext:^(id x) {
if ([x boolValue] == YES) {
// 正在执行
NSLog(@"正在执行");
}else{
// 执行完成
NSLog(@"执行完成");
}
}];
// 5.执行命令
[_conmmand execute:@1];
6.RACMulticastConnection
RACMulticastConnection:用于当一个信号,被多次订阅时,为了保证创建信号时,避免多次调用创建信号中的block,造成副作用,可以使用这个类处理。
使用注意:RACMulticastConnection通过RACSignal的-publish或者-muticast:方法创建.
// 需求:假设在一个信号中发送请求,每次订阅一次都会发送请求,这样就会导致多次请求。
//1.创建请求信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"发送请求");
[subscriber sendNext:@"我是测试"];
return nil;
}];
//2.订阅信号
[signal subscribeNext:^(id x) {
NSLog(@"接收数据1->%@", x);
}];
[signal subscribeNext:^(id x) {
NSLog(@"接收数据2->%@", x);
}];
// 3.运行结果,会执行两遍发送请求,也就是每次订阅都会发送一次请求
// 解决:使用RACMulticastConnection就能解决.
// RACMulticastConnection:解决重复请求问题
// 1.创建信号
RACSignal *signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"发送请求1");
[subscriber sendNext:@"haha"];
return nil;
}];
//2.创建连接
RACMulticastConnection *multicastConnection = [signal1 publish];
//3.订阅信号
// 注意:订阅信号,也不能激活信号,只是保存订阅者到数组,必须通过连接,当调用连接,就会一次性调用所有订阅者的sendNext:
[multicastConnection.signal subscribeNext:^(id x) {
NSLog(@"订阅者1 信号->%@", x);
}];
[multicastConnection.signal subscribeNext:^(id x) {
NSLog(@"订阅者2 信号->%@", x);
}];
//4.连接,激活信号
[multicastConnection connect];