ReactiveCocoa----ReactiveObjc

RactiveCocoa,是 Github 的一個開源框架坷随,能夠幫我們提供大量方便的事件處理方案房铭,讓我們更簡單粗暴地去處理事件,現(xiàn)在分為 ReactiveObjCReactiveSwift甸箱,兩個框架的功能使用相似育叁,本文主要是針對ReactiveObjC做一個學(xué)習(xí)記錄。

RAC雖然最大的優(yōu)點是提供了一個單一的芍殖、統(tǒng)一的方法去處理異步的行為豪嗽,包括delegate方法,blocks回調(diào)target-action機制,notificationsKVO.

RACSignal的使用及底層實現(xiàn)

1、創(chuàng)建信號:把didSubscribe(代碼塊)保存到信號中

2豌骏、訂閱信號:調(diào)用signal的subscribeNext:(void (^)(id x))nextBlock

3龟梦、調(diào)用RACDynamicSignaldidSubscribe把訂閱者傳遞過去

4、[subscriber sendNext:@1];調(diào)用訂閱者的發(fā)送消息的方法窃躲,發(fā)送消息

5计贰、執(zhí)行nextBlock這個代碼塊

RACSignal

RACSignal是信號類,當(dāng)被訂閱后蒂窒,該信號會傳遞改變的數(shù)據(jù)躁倒,可以傳遞一下三種狀態(tài)

  • sendNext(id):傳遞正確的數(shù)據(jù),告訴訂閱者下一步要干的事情
  • sendError:傳遞數(shù)據(jù)錯誤洒琢,告訴訂閱者錯誤
  • sendCompleted:告訴訂閱者發(fā)送完成秧秉,信號又變成冷信號了
  • RACDisposable對象,這個的作用就是衰抑,可以用來手動移除訂閱
  • subscribeOn指定訂閱后立即執(zhí)行的block在哪個線程中執(zhí)行
  • deliveOn指定subscribeNext在指定線程中執(zhí)行
- (void)useSingle{
    //創(chuàng)建信號并發(fā)送消息
   RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"將消息發(fā)送給訂閱著"];
        [subscriber sendError:[NSError errorWithDomain:@"給訂閱者發(fā)送錯誤信息" code:1001 userInfo:nil]];
        [subscriber sendCompleted];//告訴訂閱者已經(jīng)發(fā)送完成象迎,跟sendError不能同時使用
        return nil;//需要手動移除訂閱的話這里就返回RACDisposable類型的對象
       return [RACDisposable disposableWithBlock:^{
           //需要手動移除的時候使用這個return
       }];
    }];
    //創(chuàng)建訂閱者
    RACDisposable *disposable = [signal subscribeNext:^(id x) {
        //處理發(fā)送過來的消息
    }error:^(NSError *error) {
        //錯誤
    }completed:^{
        //完成
    }];
    [disposable dispose];//手動移除訂閱
}

總結(jié):

  • createSignal的block在創(chuàng)建的時候是冷信號不會掉用,當(dāng)訂閱者subscribeNext之后才會把冷信號變成熱信號呛踊,從而執(zhí)行createSignal的block的block
  • sendNext執(zhí)行完之后才會調(diào)用subscribeNext 的block

RACSubject

RACSubject 繼承自RACSignal,是熱信號砾淌,既可以充當(dāng)信號也可以發(fā)送信號啦撮,并不確定什么時候終止

    // 1.創(chuàng)建信號
    RACSubject *subject = [RACSubject subject];
    
    // 2.訂閱信號
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"x = %@", x);
    }];
    
    // 3.發(fā)送數(shù)據(jù)
    [subject sendNext:@10];

總結(jié):

  • 創(chuàng)建subject的時候,內(nèi)部會創(chuàng)建一個數(shù)組subscribers用來存放所有的訂閱者
  • 訂閱信息的時候會創(chuàng)建訂閱者汪厨,并保存到數(shù)組中
  • 會根據(jù)訂閱者存入subscribers的順序依次發(fā)送消息
  • RACSubject可以被多次訂閱赃春,并且只能是先訂閱后發(fā)布,因為訂閱者收不到訂閱之前的消息

RACReplaySubject

RACReplaySubject旨在解決RACSubject不能先發(fā)送消息后訂閱的問題

    // 1.創(chuàng)建信號
    RACReplaySubject *replaySubject = [RACReplaySubject subject];
      // 2.發(fā)送數(shù)據(jù)
    [replaySubject sendNext:@10];
    // 3.訂閱信號
    [replaySubject subscribeNext:^(id  _Nullable x) {
        NSLog(@"x = %@", x);
    }];
   

實現(xiàn)原理:

  • RACReplaySubject繼承自RACSubject,在創(chuàng)建對象的時候骄崩,在父類的基礎(chǔ)上會創(chuàng)建一個數(shù)組保存要發(fā)送的數(shù)據(jù)聘鳞,且在subscribers中的訂閱者全部都發(fā)送數(shù)據(jù)之后,就會刪除當(dāng)前要發(fā)送的數(shù)據(jù)
  • 訂閱者訂閱時才會發(fā)送數(shù)據(jù)
  • 訂閱信號之后要拂,先遍歷一次保存數(shù)據(jù)的數(shù)組抠璃,然后發(fā)送數(shù)據(jù)

RACCommand

主要用于網(wǎng)絡(luò)請求或者UI交互后立即執(zhí)行的流程(eg:textfield輸入內(nèi)容后處理

初始化方法

  • - (instancetype)initWithSignalBlock:(RACSignal<ValueType> * (^)(InputType _Nullable input))signalBlock;
  • - (instancetype)initWithEnabled:(nullable RACSignal<NSNumber *> *)enabledSignal signalBlock:(RACSignal<ValueType> * (^)(InputType _Nullable input))signalBlock;
    執(zhí)行方法
  • - (RACSignal<ValueType> *)execute:(nullable InputType)input;
- (void)RACCommandTest {
    
    // 1. 創(chuàng)建命令 - initWithSignalBlock
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        
        NSLog(@"input:%@",input);
        // 注意: 不能返回空的信號
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            
            [subscriber sendNext:@"input命令來了, 開始發(fā)送數(shù)據(jù)"];
            return nil;
        }];
    }];
    
    // 2. 執(zhí)行命令
    RACSignal *signal = [command execute:@"發(fā)送input 命令, 執(zhí)行command"];
    
    // 3. 訂閱信號
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"數(shù)據(jù): %@",x);
    }];
}

RACCommand屬性后期添加

RAC---rac_liftselector

這個方法的主要作用就是保證多個信號都返回數(shù)據(jù)才會執(zhí)行對應(yīng)的方法(調(diào)用多個接口,拿到所有的返回值后刷新UI

    RACSignal *firstSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"第一次獲取到的網(wǎng)絡(luò)數(shù)據(jù)"];
        return nil;
    }];
    RACSignal *secondSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"第二次獲取到的網(wǎng)絡(luò)數(shù)據(jù)"];
        return nil;
    }];
//updateUIWithData1:Data2:在所有信號都返回數(shù)據(jù)后執(zhí)行
//參數(shù)data1脱惰,data2必須與firstSignal, secondSignal相對應(yīng)
    [self rac_liftSelector:@selector(updateUIWithData1:Data2:) withSignalsFromArray:@[firstSignal, secondSignal]];

RACMulticastConnection

該方法旨在解決搏嗡,信號被多次訂閱,信號的block就會多次執(zhí)行的問題

    // RACMulticastConnection 其實是一個連接類,可以實現(xiàn)不管訂閱多少次信號,信號的block 都只請求一次
    RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
       
        NSLog(@"請求數(shù)據(jù)");
        [subscriber sendNext:@"數(shù)據(jù)是回來啦"];
        return  nil;
    }];

    // 將信號轉(zhuǎn)成連接類
    RACMulticastConnection *connection = [signalA publish];
    // 訂閱連接類信號
    [connection.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"第一次連接類信號訂閱=%@", x);
    }];
    [connection.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"第二次連接類信號訂閱=%@", x);
    }];
    [connection.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"第三次連接類信號訂閱=%@", x);
    }];
    // 連接
    [connection connect];

RAC創(chuàng)建一個定時器

該部分主要介紹三個API:

interval: 每次執(zhí)行之前等待的時間.
scheduler: 定時器執(zhí)行所在的線程
leeway: 指的是一個期望的容忍時間,將它設(shè)置為1秒,意味著系統(tǒng)有可能在定時器時間到達(dá)的前1秒或者后1秒才真正觸發(fā)定時器.在調(diào)用時推薦設(shè)置一個合理的 leeway 值,需要注意,就算指定 leeway 值為 0,系統(tǒng)也無法保證完全精確的觸發(fā)時間,只是會盡可能滿足這個需求.

  • [[RACScheduler mainThreadScheduler] afterDelay:@"延時的秒數(shù)" schedule:^{ //延時后執(zhí)行的代碼塊 }];//延時函數(shù)
  • [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]]subscribeNext:^(NSDate * date) { }];//定時器拉一,每隔多少時間做一次
    • [RACSignal interval:<#(NSTimeInterval)#> onScheduler:<#(RACScheduler *)#>]

    • [RACScheduler mainThreadScheduler]//主線程

    • [RACScheduler schedulerWithPriority:(RACSchedulerPriorityHigh) name:@"線程名"]開辟一個子線程

    • subscribeNext:<#^(id x)nextBlock#>//signal訂閱信號

  • RAC(label, text) = [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] map:^NSString *(NSDate * date) { return date.description; }];//秒表采盒,利用map映射方法將原信號中的內(nèi)容映射成新的指定內(nèi)容。

map映射的理解
RAC中有
RAC中包含兩種映射方法map蔚润、flattenMap磅氨,映射方法是將原信號中的內(nèi)容映射成新的指定內(nèi)容。map的實現(xiàn)方法中可以看出是基于flattenMap方法的一層封裝

map:將會創(chuàng)建一個和原來一模一樣的信號嫡纠,只不過新的信號傳遞的值變成了block(value)
flattenMap:把原信號的內(nèi)容映射成一個新信號烦租,并return返回給一個RACStream類型數(shù)據(jù)。實際上是根據(jù)前一個信號傳遞進來的參數(shù)重新建立了一個信號除盏,這個參數(shù)叉橱,可能會在創(chuàng)建信號的時候用到,也有可能用不到者蠕。

    [[textField.rac_textSignal map:^id(id value) {
      //在這里對輸入的內(nèi)容做處理窃祝,然后將數(shù)據(jù)返回
        return [NSString stringWithFormat:@"map處理后的數(shù)據(jù)%@",value];
    }]subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];

RACScheduler

RACScheduler就是多線程的RAC封裝,底層是GCD

  • currentScheduler//獲取當(dāng)前線程
  • mainThreadScheduler//獲取主線程
  • scheduler//子線程踱侣,異步的
  • immediateScheduler//立即執(zhí)行的線程粪小,在主線程中執(zhí)行的

** RACScheduler的優(yōu)先級**

  • +(RACScheduler *)scheduler;//開辟子線程,默認(rèn)優(yōu)先級和名字
  • +(RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority;//設(shè)置優(yōu)先級抡句,默認(rèn)名字
  • +(RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name;//設(shè)置名字和優(yōu)先級

RAC--TextField相關(guān)的API

  • [[textField rac_signalForControlEvents:UIControlEventEditingChanged] subscribeNext:^(id x) {//x==textfield內(nèi)輸入的內(nèi)容 }];
  • [textField.rac_textSignal subscribeNext:^(NSString *x) { }];
    以上兩個API可以監(jiān)聽textfield輸入文字變化

RAC-- 添加手勢

  • [[tap rac_gestureSignal] subscribeNext:^(UITapGestureRecognizer * tap) { //手勢執(zhí)行的代碼塊 }];
    UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]init];
    [[tap rac_gestureSignal] subscribeNext:^(UITapGestureRecognizer * tap) {
        
    }];
    [self.view addGestureRecognizer:tap];

RAC-- Button

  • [ [[button rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(UIButton * btn) { btn.selected = !btn.selected; }];//button點擊事件
  • RAC(button, backgroundColor) = [RACObserve(button, selected) map:^UIColor *(NSNumber * selected) { return [selected boolValue] ? [UIColor redColor] : [UIColor greenColor]; }];//利用KVO宏定義檢測按鈕的選中狀態(tài)并修改背景色探膊,button的image同理可做

RAC-- KVO及宏定義

RACObserve宏是KVO在RAC中的實現(xiàn)形式

  • [RACObserve(scrollView, contentOffset) subscribeNext:^(id x) { }];//監(jiān)控scrolleView的偏移量
  • RACObserve(button, selected)subscribeNext:^(id x) { }];//監(jiān)控button的選中狀態(tài)
  • RAC(button, backgroundColor)//用于給某個對象的某個屬性綁定。通過RAC宏設(shè)置button的背景色玉转,當(dāng)按鈕狀態(tài)改變時會動態(tài)切換,包括圖片殴蹄、文字等屬性的修改
    元組就是OC中的數(shù)組
  • RACTuplePack:把數(shù)據(jù)包裝成RACTuple(元組類
  • RACTupleUnpack:把RACTuple(元組類)解包成對應(yīng)的數(shù)據(jù)究抓。

RAC-- 代理

對于系統(tǒng)view猾担,可以使用這種方式代替
自定義view,建議直接用RACSubject

#pragma mark 代理
- (void)delegateTest
{
    UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:@"RAC" message:@"ReactiveCocoa" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Ensure", nil];
    
    [[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)] subscribeNext:^(RACTuple * tuple) {
        
        LxDBAnyVar(tuple);
        LxDBAnyVar(tuple.first);
        LxDBAnyVar(tuple.second);
        LxDBAnyVar(tuple.third);
    }];
    [alertView show];
    
    
    //  更簡單的方式:
    [[alertView rac_buttonClickedSignal]subscribeNext:^(id x) {
        
        LxDBAnyVar(x);
    }];

}

RAC-- Notification

  • [[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"通知名" object:nil] subscribeNext:^(NSNotification * notification) { }];//RAC的通知要注意有些情況需要自己管理觀察者刺下,具體可以看下面這個大佬寫的
    https://blog.csdn.net/jianghui12138/article/details/82466412
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末绑嘹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子橘茉,更是在濱河造成了極大的恐慌工腋,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件畅卓,死亡現(xiàn)場離奇詭異擅腰,居然都是意外死亡,警方通過查閱死者的電腦和手機翁潘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門趁冈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拜马,你說我怎么就攤上這事渗勘。” “怎么了俩莽?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵旺坠,是天一觀的道長。 經(jīng)常有香客問我扮超,道長取刃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任瞒津,我火速辦了婚禮蝉衣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘巷蚪。我一直安慰自己病毡,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布屁柏。 她就那樣靜靜地躺著啦膜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪淌喻。 梳的紋絲不亂的頭發(fā)上僧家,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音裸删,去河邊找鬼八拱。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肌稻。 我是一名探鬼主播清蚀,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼爹谭!你這毒婦竟也來了枷邪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤诺凡,失蹤者是張志新(化名)和其女友劉穎东揣,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腹泌,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡嘶卧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了真屯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脸候。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖绑蔫,靈堂內(nèi)的尸體忽然破棺而出运沦,到底是詐尸還是另有隱情,我是刑警寧澤配深,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布携添,位于F島的核電站,受9級特大地震影響篓叶,放射性物質(zhì)發(fā)生泄漏烈掠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一缸托、第九天 我趴在偏房一處隱蔽的房頂上張望左敌。 院中可真熱鬧,春花似錦俐镐、人聲如沸矫限。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叼风。三九已至,卻和暖如春棍苹,著一層夾襖步出監(jiān)牢的瞬間无宿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工枢里, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留孽鸡,地道東北人蹂午。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像彬碱,于是被迫代替她去往敵國和親画侣。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容