欢迎大家来到IT世界,在知识的湖畔探索吧!
《ReactiveCocoa 概述》
《RACSignal》
《RACDisposable》
《RACSubject、RACReplaySubject(内附冷信号和热信号的区别)》
《集合RACTuple、RACSequence》
《RAC 中的通知、代理、KVO, 基本事件、方法的监听》
《rac_liftSelector》
《RACMulticastConnection》
《RACCommand》
《RAC – 核心方法bind》
《RAC – 定时器》
《RACScheduler》
《RAC – 点击获取验证码 demo》
《RAC – 映射(Map & flattenMap)》
《RAC信号操作解释合集》
《RAC – 信号的生命周期》
1. 简介
ReactiveCocoa (简称为RAC)是由Github开源的一个应用于iOS和OS开发的新框架, 是基于响应式编程思想的Objective-C的实践, Cocoa则是苹果整套框架的简称.
2. 编程思想
结合了以下两种编程风格:
函数式编程(Functional Programming):
把操作尽量写成一系列嵌套的函数或者方法调用.每个方法必须有返回值(本身对象),把函数或者Block当做参数,block参数(需要操作的值)block返回值(操作结果), 即每一步都需要有结果.
响应式编程(Reactive Programming):
不需要考虑调用顺序,只需要知道考虑结果, 即一个改变就会使结果改变.典型例子(AutoLayout): aView 上添加的view, 当aView 约束发生变化时, 的view 也会随之改变.
所以, ReactiveCocoa被描述为函数响应式编程框架.
3. 如何导入ReactiveCocoa框架
通常都会使用CocoaPods导入,
PS: iOS-Cocoapods 的正确安装姿势
注意: (大小写字母一点不要写错)
ReactiveObjC — 对应的是RAC的OC版本
ReactiveCocoa–对应的是RAC的swift版本
3.1 纯OC项目
pod "ReactiveObjC"
欢迎大家来到IT世界,在知识的湖畔探索吧!
3.2 OC和Swift的混合项目
欢迎大家来到IT世界,在知识的湖畔探索吧! pod "ReactiveObjC"
pod "ReactiveCocoa"
pod "ReactiveObjCBridge"
3.3 纯Swift项目
pod "ReactiveCocoa"
4. ReactiveCocoa常见类
注: 该图片来源于网络.
.End
————–
RACSignal: 信号类, 本身不具备发送信号的能力, 当被订阅后, 用于传递改变的数据
- sendNext(id):可理解为传递正确数据,告诉订阅者进行下一步处理
- sendError:传递的数据错误,告诉订阅者错误处理
- sendCompleted:告诉订阅者已完成
欢迎大家来到IT世界,在知识的湖畔探索吧!- (void)signalTest { /* 1. 创建信号 signal - 通过 createSignal: 方法创建,其参数为一个返回值位RACDisposable 类型的block (didSubcribe) */ RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { // 2. 通过block 传入的订阅者subscriber 来发送信息 [subscriber sendNext:@10]; // sendError、sendCompleted 二者只能发送其一, 就代表结束了. [subscriber sendError:[NSError errorWithDomain:@"错误" code:1001 userInfo:nil]]; [subscriber sendCompleted]; // 这里需要返回一个RACDisposable 类型的对象, 用于提前结束订阅等操作, 一般无特殊需求, 返回nil 即可. return nil; }]; /* 3. 订阅者 (subscriber) - subscribeNext + error + completed 组合起来就是订阅者 - 一旦订阅者订阅了信号消息, 就会执行上面的didSubcribe 的block. */ [signal subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); } error:^(NSError * _Nullable error) { NSLog(@"ERROR=%@", error); } completed:^{ NSLog(@"完成"); }];}
RAC 中的通知、代理、KVO, 基本事件、方法的监听
rac_addObserverForName(通知)
OC正常写法(需要自己单独实现noti: 方法去处理通知事件)
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(noti:) name:@"noti" object:nil];
OC 中使用RAC 写法
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"noti" object:nil] subscribeNext:^(NSNotification * _Nullable x) {
// 处理通知事件
}];
↓rac_addObserverForName: 原理分析↓
- (RACSignal *)rac_addObserverForName:(NSString *)notificationName object:(id)object {
@unsafeify(object);
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
@strongify(object);
// 内部封装的 KVO
id observer = [self addObserverForName:notificationName object:object queue:nil usingBlock:^(NSNotification *note) {
// 发送监听信息
[subscriber sendNext:note];
}];
return [RACDisposable disposableWithBlock:^{
// 在取消订阅后, 移除观察者
[self removeObserver:observer];
}];
}] setNameWithFormat:@"-rac_addObserverForName: %@ object: <%@: %p>", notificationName, [object class], object];
}
- 代理
RACSubject 通常用来代替代理
rac_signalForSelector 也可以用作代理, 但是无法传值!
这里只说RACSubject , rac_signalForSelector 在下文中有详细介绍.
- 首先创建一个RedView的类继承于UIView
RedView.h
#import <UIKit/UIKit.h>
#import <ReactiveObjC/ReactiveObjC.h>
NS_ASSUME_NONNULL_BEGIN
@interface RedView : UIView
// rac 实现代理
@property (nonatomic, strong)RACSubject *subject;
@end
NS_ASSUME_NONNULL_END
RedView.m
#import "RedView.h"
@implementation RedView
- (RACSubject *)subject{
if(!_subject){
_subject = [RACSubject subject];
}
return _subject;
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.subject sendNext:@"红色视图"];
}
@end
控制器中代码:
#import "ViewController.h"
#import <ReactiveObjC/ReactiveObjC.h>
#import "RedView.h"
@interface ViewController ()
@property (nonatomic, strong)RedView *red;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self delegateTest];
}
- (void)delegateTest {
self.red = [[RedView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
self.red.backgroundColor = [UIColor redColor];
[self.view addSubview:self.red];
[self.red.subject subscribeNext:^(id _Nullable x) {
NSLog(@"代理: %@", x);
}];
}
- KVO
需要引入 #import <NSObject+RACKVOWrapper.h>头文件
方式一:
- (void)RAC_KVOTest1 {
self.kvoView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
self.kvoView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.kvoView];
[self.kvoView rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
NSLog(@"监听到最新frame - %@", value);
}];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.kvoView.frame = CGRectMake(50, 50, 50, 50);
}
输出结果: 监听到最新frame – NSRect: {{50, 50}, {50, 50}}
方式二:
- (void)RAC_KVOTest2 {
self.kvoView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
self.kvoView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.kvoView];
[[self.kvoView rac_valuesForKeyPath:@"frame" observer:self] subscribeNext:^(id _Nullable x) {
// 刚开始运行, 就会先打印一遍原始值 -> 监听到最新frame - NSRect: {{100, 100}, {100, 100}}
NSLog(@"监听到最新frame - %@", x);
// 然后点击打印 -> 监听到最新frame - NSRect: {{50, 50}, {50, 50}}
}];
}
方式三:
- (void)RAC_KVOTest3 {
self.kvoView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
self.kvoView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.kvoView];
// 使用RAC 封装的宏定义, 打印结果和方式2 一样
[RACObserve(self.kvoView, frame) subscribeNext:^(id _Nullable x) {
NSLog(@"监听到最新frame - %@", x);
}];
}
注意: 方式2、3中有注释提到, 在监听过程中会出现先打印一次原始数据值
- rac_signalForSelector(监听方法)
- (void)rac_signalForSelectorTest {
[[self rac_signalForSelector:@selector(touchesBegan:withEvent:)] subscribeNext:^(RACTuple * _Nullable x) {
NSLog(@"监听屏幕点击方法: %@", x);
}];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
}
也可以用作代理的使用, 比如检测上面RedView 的点击事件
[[self.red rac_signalForSelector:@selector(touchesBegan:withEvent:)] subscribeNext:^(RACTuple * _Nullable x) {
NSLog(@"控制器知道RedView被点击了");
}];
- rac_signalForControlEvents(监听事件)
- (void)rac_signalForControlEventsTest {
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(200, 200, 100, 50)];
button.backgroundColor = [UIColor blueColor];
[self.view addSubview:button];
[[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
NSLog(@"按钮被点击了");
}];
}
- rac_textSignal(监听textfield输入)
- (void)rac_textSignalTest {
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(200, 200, 100, 50)];
textField.layer.borderColor = [UIColor redColor].CGColor;
textField.layer.borderWidth = 1;
textField.layer.cornerRadius = 5;
textField.clipsToBounds = YES;
[self.view addSubview:textField];
[textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
// x:是最新文本数据
NSLog(@"%@", x);
}];
}
- RAC 的宏
RAC(<#TARGET, ...#>)
RAC(对象,对象的属性) = (一个信号);
比如:RAC(button, enable) = (RACSignal) 按钮的enable 等于一个信号
.End
RACSubject、RACReplaySubject(内附冷信号和热信号的区别)
- RACSubject
RACSignal 是冷信号, 不能够自己发送信号, 需要订阅者订阅, 特点是确定未来, 也就是知道什么时候结束/终止, 无视订阅者, 不管谁订阅, 都是从头开始执行一段老代码
帮助理解: 冷信号 相当于 剧本, 当订阅者订阅时, 就相当于开始拍戏, 不管谁订阅, 都是从头开始拍, 拍完了也就结束了.
RACSubject 继承自RACSignal, 是热信号, 也就是说既可以充当信号,也可以发送信号, 并不确定什么时候终止, 关心订阅者, 先来先得、后来少得
帮助理解: 热信号 相当于 拍好的戏, 当订阅者订阅时, 就开始演戏, 接下来再有订阅者订阅, 演到哪里就继续演, 不会重新从头开始, 即先来的看得多、后来看的就少.
代码分析:
// 1.创建信号
RACSubject *subject = [RACSubject subject];
// 2.订阅信号
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"x = %@", x);
}];
// 3.发送数据
[subject sendNext:@10];
↓[RACSubject subject] 内部实现↓
+ (instancetype)subject {
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (self == nil) return nil;
// 创建一个 复合的_disposable 对象, 用于取消订阅
_disposable = [RACCompoundDisposable compoundDisposable];
// 创建一个 _subscribers 订阅者集合, 用来保存订阅者
_subscribers = [[NSMutableArray alloc] initWithCapacity:1];
return self;
}
↓ 订阅信号 subscribeNext: 内部实现↓
其实内部也RACSignal 信号订阅大致相同, 唯一不同的是subscribe: 方法的实现:
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
NSCParameterAssert(subscriber != nil);
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
NSMutableArray *subscribers = self.subscribers;
@synchronized (subscribers) {
// 注意: 每次订阅, 都将订阅者添加至 信号内部的_subscribers 数组中
[subscribers addObject:subscriber];
}
[disposable addDisposable:[RACDisposable disposableWithBlock:^{
@synchronized (subscribers) {
// Since newer subscribers are generally shorter-lived, search
// starting from the end of the list.
NSUInteger index = [subscribers indexOfObjectWithOptions:NSEnumerationReverse passingTest:^ BOOL (id<RACSubscriber> obj, NSUInteger index, BOOL *stop) {
return obj == subscriber;
}];
if (index != NSNotFound) [subscribers removeObjectAtIndex:index];
}
}]];
return disposable;
}
↓ 发布消息sendNext: 内部实现↓
- (void)sendNext:(id)value {
// 遍历_subscribers 集合, 向每一个订阅者发送sendNext: 消息, 即所有订阅者依次发送消息
[self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:value];
}];
}
总结
创建的subject的是内部会创建一个数组_subscribers用来保存所有的订阅者订阅信息的时候会创建订阅者,并且保存到数组中遍历subject中_subscribers中的订阅者,依次发送信息
注意
RACSubject 可以被订阅多次,并且只能是先订阅后发布, 因为:先发送, 再有订阅者, 订阅者收不到订阅之前的消息, 所以称之为先来先得, 晚来少得
- RACReplaySubject
针对RACSubject 的注意点, 偏偏要先发送消息, 再去订阅信号, 该怎么办呢???
这里就可以使用RACReplaySubject , 它继承自RACSubject, 目的就是来解决先发送信号后订阅的问题.
代码分析: 和RACSubject 的使用一毛一样
// 先发送信号
[replaySubject sendNext:@10];
// 后订阅信号
[replaySubject subscribeNext:^(id _Nullable x) {
// 可以正常打印
NSLog(@"x = %@", x);
}];
↓具体实现原理↓
RACReplaySubject 对象创建的时候, 会在父类的基础之上多做一步,创建一个数组用来保存发送的数据(当_subscribers 中所有订阅者都成功发送了数据, 那么就会删除当前要发送的数据, 避免出现一个数据重复发送的问题)发送数据: 当有订阅者订阅时,发送数据; 没有订阅者订阅时发送失败, 就不发送,等待新的订阅者.订阅信号, 先遍历一次保存数据的数组, 如果有就执行第二步
.End
作者:下班不写程序
来源:简书
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/17541.html