ReactiveCocoa应用篇(一)

作为函数式编程和响应式编程的代表ReactiveCocoa,自一出世便受到广大码农的热爱,结合MVVM编程模式,不仅能够方便的处理各种交互事件、降低模块间的耦合性,还能简化项目结构,降低阅读和维护的成本。本文从应用的角度,介绍一下ReactiveCocoa常用类的用法:

一、RACSignal

1. 功能

这是ReactiveCocoa的核心类,用来表示数据传递,只要有数据变化,信号内部受到数据后,就会马上发出数据

2. 使用步骤

  • 创建信号
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
  • 订阅信号
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
  • 发送信号
- (void)sendNext:(id)value

具体使用如下:

	// RACSignal底层实现:
	// 1.创建信号,首先把didSubscribe保存到信号中,还不会触发。
	// 2.当信号被订阅,也就是调用signal的subscribeNext:nextBlock
	// 2.2 subscribeNext内部会创建订阅者subscriber,并且把nextBlock保存到subscriber中。
	// 2.1 subscribeNext内部会调用siganl的didSubscribe
	// 3.siganl的didSubscribe中调用[subscriber sendNext:@1];
	// 3.1 sendNext底层其实就是执行subscriber的nextBlock
	
	// 1.创建信号
	RACSignal *siganl = [RACSignal createSignal:^RACDisposable *(id subscriber) {
	
		// block调用时刻:每当有订阅者订阅信号,就会调用block。
		
		// 2.发送信号
		[subscriber sendNext:@1];
		
		// 如果不在发送数据,最好发送信号完成,内部会自动调用[RACDisposable disposable]取消订阅信号。
		[subscriber sendCompleted];
		
		// 执行完信号后进行的清理工作,如果不需要就返回 nil
		return [RACDisposable disposableWithBlock:^{
		
			// block调用时刻:当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号。
			
			// 执行完Block后,当前信号就不在被订阅了。
			
			NSLog(@"信号被销毁");
		}];
	}];
	
	// 3.订阅信号,才会激活信号.
	[siganl subscribeNext:^(id x) {
		// block调用时刻:每当有信号发出数据,就会调用block.
		NSLog(@"接收到数据:%@",x);
	}];

3. RACSubscriber

这是信号的订阅者,本质是一个协议,遵守这个协议的类对象,并且实现协议方法就可以成为一个订阅者

4. RACDisposable

这是用于取消订阅或者清理资源的类,当信号发送完成或者发送错误的时候会自动触发调用

二、RACSubject

1. 功能

信号的提供者,被成为是信号的信号,即自己可以充当信号,也能够发送信号,常用来代理代理。

2. 使用步骤

  • 创建信号
[RACSubject subject]
  • 订阅信号
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
  • 发送信号
sendNext:(id)value

具体使用如下:

	// RACSubject:底层实现和RACSignal不一样。
	// 1.调用subscribeNext订阅信号,只是把订阅者保存起来,并且订阅者的nextBlock已经赋值了。
	// 2.调用sendNext发送信号,遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock。
	
	// 1.创建信号
	RACSubject *subject = [RACSubject subject];
	
	// 2.订阅信号
	[subject subscribeNext:^(id x) {
		// block调用时刻:当信号发出新值,就会调用.
		NSLog(@"第一个订阅者%@",x);
	}];
	
	[subject subscribeNext:^(id x) {
		// block调用时刻:当信号发出新值,就会调用.
		NSLog(@"第二个订阅者%@",x);
	}];
	
	// 3.发送信号
	[subject sendNext:@"1"];

替换代理:

	// 需求:
	// 1.给当前控制器添加一个按钮,modal到另一个控制器界面
	// 2.另一个控制器view中有个按钮,点击按钮,通知当前控制器
	
	步骤一:在第二个控制器.h,添加一个RACSubject代替代理。
	@interface TwoViewController : UIViewController
	
	@property (nonatomic, strong) RACSubject *delegateSignal;
	
	@end
	
	步骤二:监听第二个控制器按钮点击
	@implementation TwoViewController
	- (IBAction)notice:(id)sender {
		// 通知第一个控制器,告诉它,按钮被点了
		
		// 通知代理
		// 判断代理信号是否有值
		if (self.delegateSignal) {
			// 有值,才需要通知
			[self.delegateSignal sendNext:nil];
		}
	}
	@end
	
	步骤三:在第一个控制器中,监听跳转按钮,给第二个控制器的代理信号赋值,并且监听.
	@implementation OneViewController
	- (IBAction)btnClick:(id)sender {
		// 创建第二个控制器
		TwoViewController *twoVc = [[TwoViewController alloc] init];
		
		// 设置代理信号
		twoVc.delegateSignal = [RACSubject subject];
		
		// 订阅代理信号
		[twoVc.delegateSignal subscribeNext:^(id x) {
			NSLog(@"点击了通知按钮");
		}];
		
		// 跳转到第二个控制器
		[self presentViewController:twoVc animated:YES completion:nil];
	}
	@end

三、RACRelaySubject

1. 功能

继承自RACSubject,特点是 订阅信号发送信号 没有先后顺序,而RACSubject就必须先订阅后发送。常用解决创建信号的block会带来副作用的场合,以及限制缓存值的数量

2. 使用步骤

RACRelaySubject的使用步骤和RACSubject类似,只是 订阅信号发送信号 没有先后顺序

具体使用如下:

	// RACReplaySubject:底层实现和RACSubject不一样。
	// 1.调用sendNext发送信号,把值保存起来,然后遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock。
	// 2.调用subscribeNext订阅信号,遍历保存的所有值,一个一个调用订阅者的nextBlock
	
	// 如果想当一个信号被订阅,就重复播放之前所有值,需要先发送信号,在订阅信号。
	// 也就是先保存值,在订阅值。
	
	// 1.创建信号
	RACReplaySubject *replaySubject = [RACReplaySubject subject];
	
	// 2.发送信号
	[replaySubject sendNext:@1];
	[replaySubject sendNext:@2];
	
	// 3.订阅信号
	[replaySubject subscribeNext:^(id x) {
		NSLog(@"第一个订阅者接收到的数据%@",x);
	}];
	
	// 订阅信号
	[replaySubject subscribeNext:^(id x) {
	
	NSLog(@"第二个订阅者接收到的数据%@",x);
	}];

四、RACCommand

1. 功能

用于处理各种事件的类,可以把事件如何处理和数据如何传递,封装到这个类中,方便监控事件的执行过程,常用来处理网络请求场景

2. 使用步骤

  • 创建命令
initWithSignalBlock:(RACSignal * (^)(id input))signalBlock
  • 创建signalBlock,并返回信号

  • 执行命令

- (RACSignal *)execute:(id)input

具体使用如下:

	// 二、RACCommand使用注意:
	// 1.signalBlock必须要返回一个信号,不能传nil.
	// 2.如果不想要传递信号,直接创建空的信号[RACSignal empty];
	// 3.RACCommand中信号如果数据传递完,必须调用[subscriber sendCompleted],这时命令才会执行完毕,否则永远处于执行中。
	// 4.RACCommand需要被强引用,否则接收不到RACCommand中的信号,因此RACCommand中的信号是延迟发送的。
	
	// 三、RACCommand设计思想:内部signalBlock为什么要返回一个信号,这个信号有什么用。
	// 1.在RAC开发中,通常会把网络请求封装到RACCommand,直接执行某个RACCommand就能发送请求。
	// 2.当RACCommand内部请求到数据的时候,需要把请求的数据传递给外界,这时候就需要通过signalBlock返回的信号传递了。
	
	// 四、如何拿到RACCommand中返回信号发出的数据。
	// 1.RACCommand有个执行信号源executionSignals,这个是signal of signals(信号的信号),意思是信号发出的数据是信号,不是普通的类型。
	// 2.订阅executionSignals就能拿到RACCommand中返回的信号,然后订阅signalBlock返回的信号,就能获取发出的值。
	
	// 五、监听当前命令是否正在执行executing
	
	// 六、使用场景,监听按钮点击,网络请求
	// 1.创建命令
	RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
		NSLog(@"执行命令");
		
		// 创建空信号,必须返回信号
		// return [RACSignal empty];
		
		// 2.创建信号,用来传递数据
		return [RACSignal createSignal:^RACDisposable *(id subscriber) {
			[subscriber sendNext:@"请求数据"];
			
			// 注意:数据传递完,最好调用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.执行命令
	[self.conmmand execute:@1];

五、RACMulticastConnection

1. 功能

用于当一个信号,被多次订阅时,为了保证创建信号时,避免多次调用创建信号中的block,造成副作用,可以使用这个类处理

2. 使用步骤

  • 创建信号
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
  • 创建信号
RACMulticastConnection *connect = [signal publish];
  • 订阅信号
[connect.signal subscribeNext:nextBlock]
  • 连接发送信号
[connect connect]

具体使用步骤:

	// RACMulticastConnection底层原理:
	// 1.创建connect,connect.sourceSignal -> RACSignal(原始信号) connect.signal -> RACSubject
	// 2.订阅connect.signal,会调用RACSubject的subscribeNext,创建订阅者,而且把订阅者保存起来,不会执行block。
	// 3.[connect connect]内部会订阅RACSignal(原始信号),并且订阅者是RACSubject
	// 3.1.订阅原始信号,就会调用原始信号中的didSubscribe
	// 3.2 didSubscribe,拿到订阅者调用sendNext,其实是调用RACSubject的sendNext
	// 4.RACSubject的sendNext,会遍历RACSubject所有订阅者发送信号。
	// 4.1 因为刚刚第二步,都是在订阅RACSubject,因此会拿到第二步所有的订阅者,调用他们的nextBlock
	
	// RACMulticastConnection:解决重复请求问题
	// 1.创建信号
	RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
	
		NSLog(@"发送请求");
		[subscriber sendNext:@1];
		
		return nil;
	}];
	
	// 2.创建连接
	RACMulticastConnection *connect = [signal publish];
	
	// 3.订阅信号,
	// 注意:订阅信号,也不能激活信号,只是保存订阅者到数组,必须通过连接,当调用连接,就会一次性调用所有订阅者的sendNext:
	[connect.signal subscribeNext:^(id x) {
		NSLog(@"订阅者一信号");
	}];
	
	[connect.signal subscribeNext:^(id x) {
		NSLog(@"订阅者二信号");
	}];
	
	// 4.连接,激活信号
	[connect connect];

未完待续...

原文地址:https://www.cnblogs.com/fishbay/p/7204680.html