一、简介
ReactiveCocoa 可以说是联合了函数式编程和相应式编程的框架,也可称其为函数相应式编程(FRP)框架,夸大一点,RAC 最大的优点是提供了一个单一的、同一的方法行止理处罚异步的举动,包括 delegate 方法, blocks 回调,target-action 机制,notifications 和 KVO。
导入
在项目的 podfile 文件中添加
# RAC pod 'ReactiveObjC'在利用时导入
#import <ReactiveObjC/ReactiveObjC.h>二、根本利用
1.button 添加点击变乱
[[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) { //button点击 }];2.取代 KVO 监听
#import "ViewController.h"#import <ReactiveObjC/ReactiveObjC.h>@interface ViewController ()@property(nonatomic,assign)NSInteger num;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; UIButton *button = [[UIButton alloc]initWithFrame:CGRectMake(50, 100, 100, 100)]; button.backgroundColor = [UIColor orangeColor]; @weakify(self); //subscribeNext 收到信号 [[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) { @strongify(self); //button点击 self.num++; }]; [self.view addSubview:button]; //RACObserve(TARGET, KEYPATH):监听某个对象的某个属性,返回的是一个信号 [RACObserve(self,num) subscribeNext:^(id _Nullable x) { // NSLog(@"%@",x); }]; //忽略值为 1 的环境,不实验回调 [[RACObserve(self, num) ignore1]subscribeNext:^(id _Nullable x) { NSLog(@"忽略%@",x); }]; }@end3.监听输入变革
self.textField = [[UITextField alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; [self.view addSubview:self.textField]; [[self.textField rac_textSignal] subscribeNext:^(NSString * _Nullable x) { NSLog(@"%@",x); }];4.关照回调
//RAC监听回调 [[[NSNotificationCenter defaultCenter] rac_addObserverForName"DJTESTNOTI" object:nil] subscribeNext:^(NSNotification * _Nullable x) { NSLog(@"x===%@",x); }]; //发送关照 [[NSNotificationCenter defaultCenter] postNotificationName"DJTESTNOTI" object"444"];5.手势回调
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init]; [self.view addGestureRecognizer:tap]; [[tap rac_gestureSignal] subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) { NSLog(@"点击"); }];6.数组和字典遍历
//数组遍历 NSArray *array = @[@"111",@"222",@"333",@"444"]; [array.rac_sequence.signal subscribeNext:^(id _Nullable x) { NSLog(@"数组%@",x); }]; //字典遍历 NSDictionary *dict = @{@"111""-111",@"222""-222",@"333""-333",@"444""-444"}; [dict.rac_sequence.signal subscribeNext:^(id _Nullable x) { NSLog(@"字典%@",x); }];二、宏
1. RAC
RAC(TARGET, [KEYPATH, [NIL_VALUE]])用于给某个对象的某个属性绑定
// 给某个对象的某个属性绑定一个信号,只要产生信号,就会把信号的内容给对象的属性举行赋值 // 给label的text属性绑定一个输入值的信号 RAC(self.titleLabel,text) = RACObserve(self, inputContentText);2.RACObserve
RACObserve(TARGET, KEYPATH)监听某个对象的某个属性,返回的是一个信号
3.RACTuplePack和RACTupleUnpack
RACTuplePack把数据包装成 RACTuple(元组类),被包装的数据必须是 object 类数据
// RACTuplePack:把一些数据包装成元组类,可用于信号间的数据传输 // 注意:被包装的数据必须是 object类数据 RACTuple *tuple = RACTuplePack(@"数据1",@1,@[@"1",@"2",@"3",@"4"]);RACTupleUnpack把 RACTuple(元组类)解包成对应的数据,解包参数的次序及数据范例要和包装数据时的次序及范例保持同等
// 参数:必要剖析数据天生出来对应的变量名 // 注意:解包参数的次序及数据范例要和包装数据时的次序及范例保持同等 RACTupleUnpack(NSString *str,NSNumber *num, NSArray *arr) = tuple;三、信号组合
1. concat
concat按肯定次序拼接信号,当多个信号发出的时间,有次序的吸收信号,依赖关系把一组信号串联起来,前面一个信号 complete,背面一个信号才开始发挥作用。
//好比A哀求依赖B哀求,只有B哀求完成之后才华实验A哀求或操纵 RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext"signalA"]; return nil; }]; RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext"signalB"]; //因为A依赖B,以是只必要在B内里声明发送完成 [subscriber sendCompleted]; return nil; }]; //注意点:如果一个操纵背面还被其他操纵依赖,好比signalB,必要在其内部发送完数据后声明发送完成,[subscriber sendCompleted]; RACSignal *contatSignal = [signalB concat:signalA]; [contatSignal subscribeNext:^(id x) { NSLog(@"(concat)效果:%@",x); //输出效果 }];2.then
then用于毗连两个信号,当第一个信号完成,才会毗连 then 返回的信号。
//A哀求依赖B哀求,只有B哀求完成之后才华实验A的哀求或操纵,必要注意:这个方法最后只能拿到A的值,如果B不必要传值,只必要先举行某些操纵的时间可以用then RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"signalA"]; return nil; }]; RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"signalB"]; [subscriber sendCompleted]; return nil; }]; RACSignal *thenSignal = [signalB then:^RACSignal *{ return signalA; }]; [thenSignal subscribeNext:^(id x) { NSLog(@"(then)效果:%@",x); //输出效果 }];then 与 concat 区别:then 监听不到第一个信号的值,共同点都是必须第一个信号完成,第二个信号才会激活
3. merge
merge 把多个信号归并为一个信号,任何一个信号有新值的时间就会调用。
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"signalA"]; return nil; }]; RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"signalB"]; [subscriber sendCompleted]; return nil; }]; RACSignal *mergeSignal = [signalA merge:signalB]; [mergeSignal subscribeNext:^(id x) { NSLog(@"(merge)效果:%@",x); }];4. zipWith
zipWith 把两个信号压缩成一个信号,只有当两个信号同时发出信号内容时,而且把两个信号的内容归并成一个元组,才会触发压缩流的 next 变乱。
//信号压缩,这个方法实在和rac_liftSelector本质时一样的,把多个信号压缩成一个信号,只有被压缩的信号全部发出消息时才华调用 RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"signalA"]; return nil; }]; RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"signalB"]; return nil; }]; //zipSignal是有次序的 RACSignal *zipSignal = [signalB zipWith:signalA]; [zipSignal subscribeNext:^(id x) { //x是担当到的全部数据包装成的元组 NSLog(@"(zipWith)效果:%@",x); //输出效果 /*--TIME:14:53:55.966000+0800 (zipWith)效果:<RACTwoTuple: 0x600003a2df90> ( signalB, signalA )*/ }];5. reduce
reduce信号聚合,参数必要本身添加
//多用于登录逻辑 RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"signalA"]; return nil; }]; RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"signalB"]; return nil; }]; // 聚合 // reduce:信号聚合,参数必要本身添加 // 常见的用法,(先组合再聚合)。combineLatestid<NSFastEnumeration>)signals reduceid (^)())reduceBlock // reduce中的block简介: // reduceblcok中的参数,有多少信号组合,reduceblcok就有多少参数,每个参数就是之前信号发出的内容 // reduceblcok的返回值:聚合信号之后的内容。 RACSignal *combineSignal = [RACSignal combineLatest:@[signalA, signalB] reduce:^id(NSString *signalA, NSString *signalB){ NSLog(@"%@----%@", signalA, signalB); //block:只要恣意一个信号发出内容,就会调用 //block参数个数:由信号决定 //block参数范例:block的参数就是信号发出值 //把两个信号中的值聚合成哪个值 return @(signalA.length && signalB.length); }]; [combineSignal subscribeNext:^(id x) { NSLog(@"(combineLatest)效果:%@",x); //输出效果 //--TIME:14:53:55.966000+0800 (combineLatest)效果:1 }]; [[signalA combineLatestWith:signalB] subscribeNext:^(id _Nullable x) { //x是吸收两个信号归并后的数据包装成的元组(RACTuple) NSLog(@"(combineLatestWith)效果:%@",x); //输出效果 /*--TIME:14:53:55.966000+0800 (combineLatestWith)效果:<RACTwoTuple: 0x600003578620> ( signalA, signalB ) */ }];6.其他
combineLatest将多个信号归并起来,而且拿到各个信号的最新的值,必须每个归并的 signal 至少都有过一次 sendNext,才会触发归并的信号。
combineLatestWith 归并两个信号,当两个信号都有 sendNext 才会触发归并的信号。
四、MVVM+RAC
示比方下:
DJViewController
#import <UIKit/UIKit.h>@interface DJViewController : UIViewController@end#import "DJViewController.h"#import "DJViewModel.h"#import "DJTableViewCell.h"@interface DJViewController ()<UITableViewDataSource, UITableViewDelegate>@property (strong, nonatomic) UITableView *tableView;@property (strong, nonatomic) DJViewModel *reqVM;@end@implementation DJViewController- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; [self setUI]; [self ViewModelEvent];}#pragma mark - 界面设置- (void)setUI { self.tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 100, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 100)]; self.tableView.dataSource = self; self.tableView.delegate = self; [self.view addSubview:self.tableView];}#pragma mark - ViewModel变乱- (void)ViewModelEvent { [self.reqVM.reqCommand execute:nil]; @weakify(self); [self.reqVM.refreshUISubject subscribeNext:^(id x) { @strongify(self); [self.tableView reloadData]; }];}#pragma mark - UITableView设置- (NSInteger)tableViewUITableView *)tableView numberOfRowsInSectionNSInteger)section{ return self.reqVM.dataArray.count;}- (UITableViewCell *)tableViewUITableView *)tableView cellForRowAtIndexPathNSIndexPath *)indexPath{ DJTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"OrderCell"]; if (!cell) { cell = [[DJTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"OrderCell"]; } cell.model = self.reqVM.dataArray[indexPath.row]; return cell;}-(CGFloat)tableViewUITableView *)tableView heightForRowAtIndexPathNSIndexPath *)indexPath{ return 80;}#pragma mark - 懒加载- (DJViewModel *)reqVM { if (!_reqVM) { _reqVM = [[DJViewModel alloc] init]; } return _reqVM;}@end
- [self.reqVM.reqCommand execute:nil]; 方法为实验 reqCommand 变乱下令,reqCommand 是 DJViewModel 中网络哀求变乱。
- [self.reqVM.refreshUISubject subscribeNext:^(id x) { @strongify(self); [self.tableView reloadData]; }];
此方法为订阅 DJViewModel 中网络哀求完成时发送的信号(refreshUISubject),也就是说当网络哀求完成之后会实验 block 中的革新 tableView 方法。
DJViewModel
#import <Foundation/Foundation.h>#import <ReactiveObjC/ReactiveObjC.h>@interface DJViewModel : NSObject@property (nonatomic, strong) RACSubject *refreshUISubject;@property (strong, nonatomic) RACCommand *reqCommand;@property (nonatomic, strong) NSArray *dataArray;@end#import "DJViewModel.h"#import "DJModel.h"@interface DJViewModel ()@end@implementation DJViewModel- (instancetype)init { if (self = [super init]) { [self or_initialize]; } return self;}- (void)or_initialize { //网络哀求信号 [self.reqCommand.executionSignals.switchToLatest subscribeNext:^(NSDictionary *dic) { NSArray *items = dic[@"items"]; NSMutableArray *arr = [NSMutableArray array]; for (NSDictionary * dict in items) { DJModel *model = [[DJModel alloc]init]; model.name = dict[@"name"]; [arr addObject:model]; } self.dataArray = [arr copy]; [self.refreshUISubject sendNext:nil]; }]; [[self.reqCommand.executing skip:1] subscribeNext:^(id x) { if ([x isEqualToNumber:@(YES)]) {// [MBProgressHUD showCircleHud:nil]; }else {// [MBProgressHUD closeHud:nil]; } }];}#pragma mark - 懒加载- (RACCommand *)reqCommand { if (!_reqCommand) { _reqCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) { //因为要把哀求的数据传出去,以是要把网络哀求包装在信号里 RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { //网络哀求 NSDictionary *dic = @{@"items":@[@{@"name":@"hhhh"}]}; [subscriber sendNext:dic]; [subscriber sendCompleted]; return nil; }]; //返回网络哀求信号 return signal; }]; } return _reqCommand;}- (RACSubject *)refreshUISubject { if (!_refreshUISubject) { _refreshUISubject = [RACSubject subject]; } return _refreshUISubject;}- (NSArray *)dataArray { if (!_dataArray) { _dataArray = [[NSArray alloc] init]; } return _dataArray;}@end
- refreshUISubject 属性是关照控制器革新 UI 的信号,其功能类似于署理。reqCommand 属性是网络哀求变乱,袒露在 .h 文件的缘故原由是让控制器来决定什么时间发起变乱,也就是说什么时间发起网络哀求。
- or_initialize 中第一个方法是订阅 reqCommand(网络哀求)变乱中的信号发出的值,也就是网络哀求乐成后发送的数据。第二个方法的功能是监听 reqCommand 变乱过程,其 block 中的值返回 YES 是,代表变乱正在实验,以是在这内里可以加一个正在加载的菊花,当返回值为 NO 时,代表变乱实验完成,把正在加载菊花去掉。
- 懒加载 - (RACCommand *)reqCommand 方法中就是网络哀求变乱,block 内里的 signal 信号作用是把网络哀求的数据发送给 or_initialize 中第一个方法的订阅者。订阅者拿到数据后实验字典转模子操纵,然后发送袒露在 .h 文件中的 refreshUISubject 信号给订阅此信号的控制器,关照他革新 tableView。
DJViewModel、DJTableViewCell
#import <Foundation/Foundation.h>@interface DJModel : NSObject@property(nonatomic,assign)NSInteger num;@end#import "DJModel.h"@implementation DJModel@end#import <UIKit/UIKit.h>#import "DJModel.h"@interface DJTableViewCell : UITableViewCell@property (nonatomic, strong) DJModel *model;@end#import "DJTableViewCell.h"@interface DJTableViewCell ()@end@implementation DJTableViewCell- (void)setModelDJModel *)model { self.textLabel.text = model.name;}@end |