iOS -RAC使用

一、簡介

ReactiveCocoa 可以說是結合了函數(shù)式編程和響應式編程的框架,也可稱其為函數(shù)響應式編程(FRP)框架,強調(diào)一點,RAC 最大的優(yōu)點是提供了一個單一的、統(tǒng)一的方法去處理異步的行為,包括 delegate 方法, blocks 回調(diào),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 監(jiān)聽
#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):監(jiān)聽某個對象的某個屬性,返回的是一個信號
    [RACObserve(self,num) subscribeNext:^(id  _Nullable x) {
        //
        NSLog(@"%@",x);
    }];

    //忽略值為 1 的情況,不執(zhí)行回調(diào)
    [[RACObserve(self, num) ignore:@1]subscribeNext:^(id  _Nullable x) {
        NSLog(@"忽略%@",x);
    }];
 
}
@end
3.監(jiān)聽輸入變化
    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.通知回調(diào)
    //RAC監(jiān)聽回調(diào)
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"DJTESTNOTI" object:nil] subscribeNext:^(NSNotification * _Nullable x) {
            NSLog(@"x===%@",x);
    }];
    
    //發(fā)送通知
    [[NSNotificationCenter defaultCenter] postNotificationName:@"DJTESTNOTI" object:@"444"];
5.手勢回調(diào)
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
    [self.view addGestureRecognizer:tap];
    [[tap rac_gestureSignal] subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
        NSLog(@"點擊");
    }];
6.數(shù)組和字典遍歷
    //數(shù)組遍歷
    NSArray *array = @[@"111",@"222",@"333",@"444"];
    [array.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"數(shù)組%@",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]])用于給某個對象的某個屬性綁定

 // 給某個對象的某個屬性綁定一個信號,只要產(chǎn)生信號,就會把信號的內(nèi)容給對象的屬性進行賦值
 // 給label的text屬性綁定一個輸入值的信號
 RAC(self.titleLabel,text) = RACObserve(self, inputContentText);
2.RACObserve

RACObserve(TARGET, KEYPATH)監(jiān)聽某個對象的某個屬性,返回的是一個信號

3.RACTuplePackRACTupleUnpack

RACTuplePack把數(shù)據(jù)包裝成 RACTuple(元組類),被包裝的數(shù)據(jù)必須是 object 類數(shù)據(jù)

   // RACTuplePack:把一些數(shù)據(jù)包裝成元組類,可用于信號間的數(shù)據(jù)傳輸
   // 注意:被包裝的數(shù)據(jù)必須是 object類數(shù)據(jù)
   RACTuple *tuple = RACTuplePack(@"數(shù)據(jù)1",@1,@[@"1",@"2",@"3",@"4"]);

RACTupleUnpack把 RACTuple(元組類)解包成對應的數(shù)據(jù),解包參數(shù)的順序及數(shù)據(jù)類型要和包裝數(shù)據(jù)時的順序及類型保持一致

  // 參數(shù):需要解析數(shù)據(jù)生成出來對應的變量名
  // 注意:解包參數(shù)的順序及數(shù)據(jù)類型要和包裝數(shù)據(jù)時的順序及類型保持一致
  RACTupleUnpack(NSString  *str,NSNumber  *num, NSArray *arr) = tuple;

三、信號組合

1. concat

concat按一定順序拼接信號,當多個信號發(fā)出的時候,有順序的接收信號,依賴關系把一組信號串聯(lián)起來,前面一個信號 complete,后面一個信號才開始發(fā)揮作用。

    //比如A請求依賴B請求,只有B請求完成之后才能執(zhí)行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里面聲明發(fā)送完成
        [subscriber sendCompleted];
        return nil;
    }];
    
    //注意點:如果一個操作后面還被其他操作依賴,比如signalB,需要在其內(nèi)部發(fā)送完數(shù)據(jù)后聲明發(fā)送完成,[subscriber sendCompleted];
    RACSignal *contatSignal = [signalB concat:signalA];
    
    [contatSignal subscribeNext:^(id x) {
        NSLog(@"(concat)結果:%@",x);
        //輸出結果
        
    }];
2.then

then用于連接兩個信號,當?shù)谝粋€信號完成,才會連接 then 返回的信號。

    //A請求依賴B請求,只有B請求完成之后才能執(zhí)行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 區(qū)別:then 監(jiān)聽不到第一個信號的值,共同點都是必須第一個信號完成,第二個信號才會激活

3. merge

merge 把多個信號合并為一個信號,任何一個信號有新值的時候就會調(diào)用。

    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 把兩個信號壓縮成一個信號,只有當兩個信號同時發(fā)出信號內(nèi)容時,并且把兩個信號的內(nèi)容合并成一個元組,才會觸發(fā)壓縮流的 next 事件。

    //信號壓縮,這個方法其實和rac_liftSelector本質(zhì)時一樣的,把多個信號壓縮成一個信號,只有被壓縮的信號全部發(fā)出消息時才能調(diào)用
    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是接受到的所有數(shù)據(jù)包裝成的元組
        NSLog(@"(zipWith)結果:%@",x);
        //輸出結果
        /*--TIME:14:53:55.966000+0800
        (zipWith)結果:<RACTwoTuple: 0x600003a2df90> (
            signalB,
            signalA
        )*/
    }];
5. reduce

reduce信號聚合,參數(shù)需要自己添加

    //多用于登錄邏輯
    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:信號聚合,參數(shù)需要自己添加
    // 常見的用法,(先組合再聚合)。combineLatest:(id<NSFastEnumeration>)signals reduce:(id (^)())reduceBlock
    // reduce中的block簡介:
    // reduceblcok中的參數(shù),有多少信號組合,reduceblcok就有多少參數(shù),每個參數(shù)就是之前信號發(fā)出的內(nèi)容
    // reduceblcok的返回值:聚合信號之后的內(nèi)容。
    RACSignal *combineSignal = [RACSignal combineLatest:@[signalA, signalB] reduce:^id(NSString *signalA, NSString *signalB){
        NSLog(@"%@----%@", signalA, signalB);
        //block:只要任意一個信號發(fā)出內(nèi)容,就會調(diào)用
        //block參數(shù)個數(shù):由信號決定
        //block參數(shù)類型:block的參數(shù)就是信號發(fā)出值
        //把兩個信號中的值聚合成哪個值
        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是接收兩個信號合并后的數(shù)據(jù)包裝成的元組(RACTuple)
        NSLog(@"(combineLatestWith)結果:%@",x);
        //輸出結果
       /*--TIME:14:53:55.966000+0800
        (combineLatestWith)結果:<RACTwoTuple: 0x600003578620> (
            signalA,
            signalB
        )
        */
    }];
6.其他

combineLatest將多個信號合并起來,并且拿到各個信號的最新的值,必須每個合并的 signal 至少都有過一次 sendNext,才會觸發(fā)合并的信號。
combineLatestWith 合并兩個信號,當兩個信號都有 sendNext 才會觸發(fā)合并的信號。

四、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)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
   return self.reqVM.dataArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)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)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 80;
}
#pragma mark - 懶加載
- (DJViewModel *)reqVM {
   if (!_reqVM) {
      _reqVM = [[DJViewModel alloc] init];
   }
   return _reqVM;
}

@end
  • [self.reqVM.reqCommand execute:nil]; 方法為執(zhí)行 reqCommand 事件命令,reqCommandDJViewModel 中網(wǎng)絡請求事件。
  • [self.reqVM.refreshUISubject subscribeNext:^(id x) { @strongify(self); [self.tableView reloadData]; }];
    此方法為訂閱 DJViewModel 中網(wǎng)絡請求完成時發(fā)送的信號(refreshUISubject),也就是說當網(wǎng)絡請求完成之后會執(zhí)行 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 {
    //網(wǎng)絡請求信號
    [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) {
            //因為要把請求的數(shù)據(jù)傳出去,所以要把網(wǎng)絡請求包裝在信號里
            RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
                //網(wǎng)絡請求
                NSDictionary *dic = @{@"items":@[@{@"name":@"hhhh"}]};
                [subscriber sendNext:dic];
                [subscriber sendCompleted];
                return nil;
            }];
            //返回網(wǎng)絡請求信號
            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 屬性是網(wǎng)絡請求事件,暴露在 .h 文件的原因是讓控制器來決定什么時候發(fā)起事件,也就是說什么時候發(fā)起網(wǎng)絡請求。
  • or_initialize 中第一個方法是訂閱 reqCommand(網(wǎng)絡請求)事件中的信號發(fā)出的值,也就是網(wǎng)絡請求成功后發(fā)送的數(shù)據(jù)。第二個方法的功能是監(jiān)聽 reqCommand 事件過程,其 block 中的值返回 YES 是,代表事件正在執(zhí)行,所以在這里面可以加一個正在加載的菊花,當返回值為 NO 時,代表事件執(zhí)行完成,把正在加載菊花去掉。
  • 懶加載 - (RACCommand *)reqCommand 方法中就是網(wǎng)絡請求事件,block 里面的 signal 信號作用是把網(wǎng)絡請求的數(shù)據(jù)發(fā)送給 or_initialize 中第一個方法的訂閱者。訂閱者拿到數(shù)據(jù)后執(zhí)行字典轉(zhuǎn)模型操作,然后發(fā)送暴露在 .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)setModel:(DJModel *)model {
    self.textLabel.text = model.name;
}

@end
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容