• 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];
    

    未完待续...

  • 相关阅读:
    EF架构~系列目录
    不谈技术~做一个好人,一个简单的人
    不谈技术~谈人生,干吧,年轻人!
    c++ 用lambda删除vector中元素
    转:我们为什么这么容易受骗?
    windows 数据类型
    《暗时间》书摘 “学习与思考”
    转:斯托克代尔悖论与底线思考法
    Android之HelloWorld
    win7搭建android开发环境
  • 原文地址:https://www.cnblogs.com/fishbay/p/7204680.html
Copyright © 2020-2023  润新知