iOS ReactiveCocoa學習筆記(1):基本使用

本文知識點:RACSignal使用箫津、combine侨糟、@weakify@strongify耀态、Model與UI雙向綁定。

1. 介紹

ReactiveCocoa 接管了蘋果的事件機制轧粟,asyncDisplayKit接管了蘋果的UIKit策治。

  • 運用的是Hook(鉤子)思想,Hook是一種用于改變API(應用程序編程接口:方法)執(zhí)行結果的技術兰吟。
  • Hook用處:截獲API調用的技術通惫。
  • Hook原理:在每次調用一個API返回結果之前,先執(zhí)行你自己的方法混蔼,改變結果的輸出履腋。

1.1 響應式

使用RAC的原因:實現(xiàn)響應式編程。

  • 什么是響應式編程:
b = 2; c = 3;
a = b + c;  //a = 5;
b = 100;  //此時對修改 b 的修改并不會使 a 發(fā)生改變惭嚣。

響應式:當修改b或c的時候遵湖,a同時發(fā)生變化。

  • iOS開發(fā)中實現(xiàn)響應式
    • 方法一:使用KVO監(jiān)聽對象的屬性值達到這一效果晚吞。但缺點是KVO會統(tǒng)一調用同一個方法延旧,如果監(jiān)聽屬性過多,方法非常難以維護载矿。
    • 方法二:ReactiveCocoa是目前實現(xiàn)響應式編程的唯一解決方案垄潮。

1.2 難點

  • 學習曲線陡峭
  • 在團隊開發(fā)的時候要特別謹慎
  • 需要不斷的代碼評審,保證團隊的代碼風格一致
  • 開發(fā)的時候調用堆棧深不見底闷盔,提高debug成本

1.3 框架導入

Cocoapods導入ReactiveCocoa5.0以上版本需注意:

  • Swift 項目:使用 ReactiveCocoa
    弯洗。但是 RAC 依賴于 ReactiveSwift,等于你引入了兩個庫逢勾。
pod 'ReactiveCocoa'
  • OC 項目:使用 ReactiveObjC
    牡整。這個庫里面包含原來 RAC 2 的全部代碼。
pod 'ReactiveObjC'
pod 'ReactiveObjC'
pod 'ReactiveCocoa'
pod 'ReactiveObjCBridge'

2. 使用

2.1 RACSignal

1> 信號的創(chuàng)建

RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    [subscriber sendNext:@"123"];
    [subscriber sendNext:@"456"];
    NSLog(@"%@",subscriber);
    // 如果不再發(fā)送數據谣辞,最后發(fā)送信號完成,內部會自動調用[RACDisposable disposable]取消訂閱信號沐扳。
    [subscriber sendCompleted];
    [subscriber sendError:[NSError errorWithDomain:@"send error" code:0 userInfo:@{}]];
    return [RACDisposable disposableWithBlock:^{
        // block調用時刻:當信號發(fā)送完成或者發(fā)送錯誤泥从,就會自動執(zhí)行這個block,取消訂閱信號。
        // 執(zhí)行完Block后沪摄,當前信號就不再被訂閱了躯嫉。
        // 信號銷毀的時候 會執(zhí)行這個閉包
        // 用于取消訂閱或者清理資源,當信號發(fā)送完成或者發(fā)送錯誤的時候杨拐,就會自動觸發(fā)它祈餐。
        // 使用場景:不想監(jiān)聽某個信號時,可以通過它主動取消訂閱信號哄陶。
        NSLog(@"dispose");
    }];
}];
//信號被訂閱帆阳。訂閱者不是信號本身 而是這段代碼所處的那個objcect 如在vc中,訂閱者就是vc
[signal subscribeNext:^(id  _Nullable x) {
    NSLog(@"訂閱next :%@",x);
}];
[signal subscribeError:^(NSError * _Nullable error) {
    NSLog(@"訂閱error :%@",x);
}];
[signal subscribeCompleted:^ {
    NSLog(@"訂閱completed");
}];


Output:
訂閱next :123  //訂閱的next信號
訂閱next :456
<RACPassthroughSubscriber: 0x60400043aa80>  
dispose  
//訂閱的error信號(因為代碼順序),但是此時已無效屋吨,因為subscriber發(fā)出了completed信號
<RACPassthroughSubscriber: 0x60400043aa80>
dispose
訂閱completed
<RACPassthroughSubscriber: 0x60400043aa80>
dispose
  • RACSignal是冷信號蜒谤,只有被訂閱了才可以工作。
  • RACSubscriber:表示訂閱者的意思至扰,用于發(fā)送信號芭逝,這是一個協(xié)議,不是一個類渊胸,只要遵守這個協(xié)議,并且實現(xiàn)方法才能成為訂閱者台妆。通過create創(chuàng)建的信號翎猛,都有一個訂閱者,幫助他發(fā)送數據接剩。
  • RACDisposable:用于取消訂閱或者清理資源切厘,當信號發(fā)送完成或者發(fā)送錯誤的時候,就會自動觸發(fā)它懊缺。
    使用場景:不想監(jiān)聽某個信號時疫稿,可以通過它主動取消訂閱信號。

2> 控件的監(jiān)聽

UI類RAC自動封裝了一些方法鹃两,使用的時候先去框架源文件中找遗座。以 button 為例:

[[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
    NSLog(@"%@", x);
} error:^(NSError * _Nullable error) {
    NSLog(@"%@",error);
} completed:^{
    NSLog(@"completed");
}];

3> 信號的合并

類似元組類型,可以一次訂閱多個信號俊扳。

//信號合并
+ (RACSignal *)combineLatest:(id<NSFastEnumeration>)signals;
//reduce中可以通過接受的參數進行計算途蒋,并且返回需要的數值
+ (RACSignal *)combineLatest:(id<NSFastEnumeration>)signals reduce:(RACGenericReduceBlock)reduceBlock;


Ex1:                                                                                                         //元組類型
[[RACSignal combineLatest:@[[name_textField rac_textSignal], [pwd_textField rac_textSignal]]] subscribeNext:^(RACTuple * _Nullable x) {
    NSLog(@"%@ %@", x.first, x.second);
}];
Ex2:
//例如訂閱username、password馋记,reduce里面判斷只有用戶名和密碼同時存在才允許登錄
RACSignal *signal_username = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    [subscriber sendNext:@"abc"];
    return nil;
}];
    
RACSignal *signal_pwd = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    [subscriber sendNext:@"123"];
    return nil;
}];
                                                                       //注意 參數需要自己添加上去
[[RACSignal combineLatest:@[signal_username, signal_pwd] reduce:^id _Nonnull (NSString *username, NSString *password){
    return @(username.length > 0 && password.length > 0);
}] subscribeNext:^(id  _Nullable x) {
    //x是上面的@(bool)
    NSLog(@"%@", x);  
}];


Output: 1

4> RAC中的循環(huán)引用

因為系統(tǒng)提供的信號是始終存在的号坡,因此在RAC中所有的block中懊烤,如果出現(xiàn)self._成員變量,幾乎百分之百會循環(huán)引用宽堆。
解決辦法:weak-strong dance(RAC提供了宏 @weakify & @strongify)

2.2 RAC雙向綁定

需要用到兩個重要宏: RAC()腌紧、RACObserve(),源碼如下:

#define RAC(TARGET, ...) \
    metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
        (RAC_(TARGET, __VA_ARGS__, nil)) \
        (RAC_(TARGET, __VA_ARGS__))

#define _RACObserve(TARGET, KEYPATH) \
({ \
    __weak id target_ = (TARGET); \
    [target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]; \
})
#if __clang__ && (__clang_major__ >= 8)
#define RACObserve(TARGET, KEYPATH) _RACObserve(TARGET, KEYPATH)
#else
#define RACObserve(TARGET, KEYPATH) \
({ \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Wreceiver-is-weak\"") \
    _RACObserve(TARGET, KEYPATH) \
    _Pragma("clang diagnostic pop") \
})
#endif

1> Model -> UI (利用KVO宏)

RAC(TARGET, ...) = RACObserve(TARGET, KEYPATH);

如果使用基本數據類型綁定UI內容畜隶,需要使用map函數壁肋,通過block對value的數值進行轉換后才能夠綁定。

RAC(name_textField, text) = RACObserve(_person, name);
//rac中傳遞的數據都是id類型代箭,如果是基本類型墩划,需要使用map函數,通過block對value的數值進行轉換后才能夠綁定嗡综。
RAC(age_textField, text) = [RACObserve(_person, age) map:^id _Nullable(id  _Nullable value) {
    return [value description];
}];

2> UI -> Model (訂閱控件發(fā)出的signal)

@weakify(self);
[[RACSignal combineLatest:@[[name_tf rac_textSignal], [age_tf rac_textSignal]]] subscribeNext:^(RACTuple * _Nullable x) {
    @strongify(self);
    self.person.name = x.first;
    self.person.age = [x.second integerValue];
}];

// 或
RAC(_person, name) = name_tf.rac_textSignal;
RAC(_person, age) = age_tf.rac_textSignal;

3. MVVM+RAC

MVVM+RAC
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末乙帮,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子极景,更是在濱河造成了極大的恐慌察净,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盼樟,死亡現(xiàn)場離奇詭異氢卡,居然都是意外死亡,警方通過查閱死者的電腦和手機晨缴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門译秦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人击碗,你說我怎么就攤上這事筑悴。” “怎么了稍途?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵阁吝,是天一觀的道長。 經常有香客問我械拍,道長突勇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任坷虑,我火速辦了婚禮甲馋,結果婚禮上,老公的妹妹穿的比我還像新娘猖吴。我一直安慰自己摔刁,他們只是感情好,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著共屈,像睡著了一般绑谣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拗引,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天借宵,我揣著相機與錄音,去河邊找鬼矾削。 笑死壤玫,一個胖子當著我的面吹牛,可吹牛的內容都是我干的哼凯。 我是一名探鬼主播欲间,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼断部!你這毒婦竟也來了猎贴?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蝴光,失蹤者是張志新(化名)和其女友劉穎她渴,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體蔑祟,經...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡趁耗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了疆虚。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苛败。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖径簿,靈堂內的尸體忽然破棺而出著拭,到底是詐尸還是另有隱情,我是刑警寧澤牍帚,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站乳蛾,受9級特大地震影響暗赶,放射性物質發(fā)生泄漏。R本人自食惡果不足惜肃叶,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一蹂随、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧因惭,春花似錦岳锁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咳燕。三九已至,卻和暖如春乒躺,著一層夾襖步出監(jiān)牢的瞬間招盲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工嘉冒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留曹货,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓讳推,卻偏偏與公主長得像顶籽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子银觅,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355