首先介紹一下什么是RAC
ReactiveCocoa(簡稱為RAC),是由Github開源的一個(gè)應(yīng)用于iOS和OS開發(fā)的新框架,Cocoa是蘋果整套框架的簡稱掰读,因此很多蘋果框架喜歡以Cocoa結(jié)尾秘狞。
然后介紹一下RAC的作用叭莫,以及有哪些功能
在我們iOS開發(fā)過程中,當(dāng)某些事件響應(yīng)的時(shí)候烁试,需要處理某些業(yè)務(wù)邏輯,這些事件都用不同的方式來處理雇初。 比如按鈕的點(diǎn)擊使用action,ScrollView滾動(dòng)使用delegate减响,屬性值改變使用KVO等系統(tǒng)提供的方式靖诗。 其實(shí)這些事件,都可以通過RAC處理 ReactiveCocoa為事件提供了很多處理方法辩蛋,而且利用RAC處理事件很方便呻畸,可以把要處理的事情,和監(jiān)聽的事情的代碼放在一起悼院,這樣非常方便我們管理,就不需要跳到對(duì)應(yīng)的方法里咒循。非常符合我們開發(fā)中高聚合据途,低耦合的思想。
在這里說道RAC那就必須得說一下編程思想這個(gè)詞了
編程思想的由來
:在開發(fā)中我們會(huì)遇見各種各樣的需求叙甸,經(jīng)常會(huì)思考如何快速的完成這些需求颖医,這樣就會(huì)慢慢形成快速完成這些需求的思想。
先簡單介紹下目前咱們已知的編程思想裆蒸。
1熔萧、面向過程
:處理事情以過程為核心,一步一步的實(shí)現(xiàn)僚祷。
2佛致、面向?qū)ο?/code>:萬物皆對(duì)象
3、鏈?zhǔn)骄幊趟枷?/code>:是將多個(gè)操作(多行代碼)通過點(diǎn)號(hào)(.)鏈接在一起成為一句代碼,使代碼可讀性好辙谜。a(1).b(2).c(3)
鏈?zhǔn)骄幊烫攸c(diǎn):方法的返回值是block,block必須有返回值(本身對(duì)象)俺榆,block參數(shù)(需要操作的值)
代表:masonry框架。
4装哆、 響應(yīng)式編程思想
:不需要考慮調(diào)用順序罐脊,只需要知道考慮結(jié)果,類似于蝴蝶效應(yīng)蜕琴,產(chǎn)生一個(gè)事件萍桌,會(huì)影響很多東西,這些事件像流一樣的傳播出去凌简,然后影響結(jié)果上炎,借用面向?qū)ο蟮囊痪湓挘f物皆是流号醉。
代表:KVO運(yùn)用反症。
練習(xí)二:KVO底層實(shí)現(xiàn)辛块。
5、函數(shù)式編程思想
:是把操作盡量寫成一系列嵌套的函數(shù)或者方法調(diào)用铅碍。
函數(shù)式編程本質(zhì):就是往方法中傳入Block,方法中嵌套Block調(diào)用润绵,把代碼聚合起來管理
函數(shù)式編程特點(diǎn):每個(gè)方法必須有返回值(本身對(duì)象),把函數(shù)或者Block當(dāng)做參數(shù),block參數(shù)(需要操作的值)block返回值(操作結(jié)果)
代表:ReactiveCocoa。
練習(xí)三:用函數(shù)式編程實(shí)現(xiàn)胞谈,寫一個(gè)加法計(jì)算器,并且加法計(jì)算器自帶判斷是否等于某個(gè)值.
#######4.ReactiveCocoa編程思想
ReactiveCocoa結(jié)合了幾種編程風(fēng)格
:
函數(shù)式編程
(Functional Programming)
響應(yīng)式編程
(Reactive Programming)
所以尘盼,你可能聽說過ReactiveCocoa被描述為函數(shù)響應(yīng)式編程
(FRP
)框架。
以后使用RAC解決問題烦绳,就不需要考慮調(diào)用順序卿捎,直接考慮結(jié)果,把每一次操作都寫成一系列嵌套的方法中径密,使代碼高聚合午阵,方便管理。
#######如何導(dǎo)入ReactiveCocoa框架
使用CocoaPods(用于管理第三方框架的插件)幫助我們導(dǎo)入享扔。
具體步驟:PS:CocoaPods教程(http://code4app.com/article/cocoapods-install-usage)
podfile如果只描述pod 'ReactiveCocoa', '~> 4.0.2-alpha-1'底桂,會(huì)導(dǎo)入不成功
報(bào)錯(cuò)信息:會(huì)提示我們說Swift什么什么玩意的。然后我們需要打開一句話惧眠,use_frameworks
因?yàn)镽AC需要用到Swift的一些東西
下面會(huì)具體介紹一下RAC中的一些類的基本使用
1.首先介紹RACSignal這個(gè)類 使用流程是 1創(chuàng)建信號(hào)
籽懦,2訂閱者訂閱信號(hào)
,3發(fā)送信號(hào)
RACSignal信號(hào)類氛魁!當(dāng)數(shù)據(jù)改變時(shí)暮顺,信號(hào)內(nèi)部接收到數(shù)據(jù),就會(huì)馬上發(fā)出數(shù)據(jù)秀存,但是RACSignal本身是不具有發(fā)送消息的功能捶码,而是交給內(nèi)部一個(gè)叫做信號(hào)訂閱者發(fā)出的!(冷信號(hào)
)
創(chuàng)建完以后必須要去訂閱信號(hào)应又!只有訂閱者把信號(hào)訂閱以后冷信號(hào)才會(huì)變熱信號(hào)宙项!
代碼實(shí)現(xiàn)
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// didSubscribe調(diào)用:只要一個(gè)信號(hào)被訂閱就會(huì)調(diào)用
// didSubscribe作用:發(fā)送數(shù)據(jù)
NSLog(@"信號(hào)被訂閱");
// 3.發(fā)送數(shù)據(jù)
[subscriber sendNext:@1];
return nil;
}];
// 2.訂閱信號(hào)(熱信號(hào))
[signal subscribeNext:^(id x) {
// nextBlock調(diào)用:只要訂閱者發(fā)送數(shù)據(jù)就會(huì)調(diào)用
// nextBlock作用:處理數(shù)據(jù),展示到UI上面
// x:信號(hào)發(fā)送的內(nèi)容
NSLog(@"%@",x);
}];
// 只要訂閱者調(diào)用sendNext,就會(huì)執(zhí)行nextBlock
// 只要訂閱RACDynamicSignal,就會(huì)執(zhí)行didSubscribe
// 前提條件是RACDynamicSignal,不同類型信號(hào)的訂閱,處理訂閱的事情不一樣
下面介紹一下RACSignal的底層實(shí)現(xiàn)原理
第一步
RACSignal在創(chuàng)建的時(shí)候會(huì)帶有一個(gè)Block這個(gè)Block什么是不被調(diào)用呢!看下一步株扛!
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// didSubscribe調(diào)用:只要一個(gè)信號(hào)被訂閱就會(huì)調(diào)用
// didSubscribe作用:發(fā)送數(shù)據(jù)
NSLog(@"信號(hào)被訂閱");
#第三步在這里
// 3.發(fā)送數(shù)據(jù)
[subscriber sendNext:@1];
發(fā)送數(shù)據(jù)的底層都幫我們做了什么呢尤筐?
看了源代碼發(fā)現(xiàn),在我們發(fā)送這個(gè)信號(hào)的時(shí)候RAC內(nèi)部會(huì)調(diào)用訂閱者去發(fā)送數(shù)據(jù)洞就!
return nil;
}];
然后我們進(jìn)行下一步盆繁,去訂閱剛剛創(chuàng)建的這個(gè)信號(hào)!
代碼如下:
[signal subscribeNext:^(id x) {
// nextBlock調(diào)用:只要訂閱者發(fā)送數(shù)據(jù)就會(huì)調(diào)用
// nextBlock作用:處理數(shù)據(jù),展示到UI上面
// x:信號(hào)發(fā)送的內(nèi)容
NSLog(@"%@",x);
}];
#######但是在訂閱的工程中我們并沒有去創(chuàng)建訂閱者旬蟋,我們只是調(diào)用了一下這個(gè)subscribeNext方法油昂,這個(gè)方法內(nèi)部幫我們做了很多事!比如說,在我們調(diào)用這個(gè)方法的時(shí)候冕碟,RAC內(nèi)部會(huì)幫我們創(chuàng)建一個(gè)訂閱者然后去訂閱我們上一步創(chuàng)建的冷信號(hào)拦惋,在訂閱后,冷信號(hào)隨之變成熱信號(hào)安寺!
最后一步厕妖,看第一步中Block內(nèi)!L羰言秸!
上面是RACSignal
的基本使用方法!
上面我們大致說來說RACSignal
這個(gè)類怎么使用迎捺!
但是在RACSignal
第一步中的Block室友返回值的可是當(dāng)時(shí)我是直接返回了一個(gè)nil
;現(xiàn)在說一下具體返回一個(gè)什么玩意举畸!
首先我們還需要看一下RACSignal
的創(chuàng)建方法
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// didSubscribe調(diào)用:只要一個(gè)信號(hào)被訂閱就會(huì)調(diào)用
// didSubscribe作用:發(fā)送數(shù)據(jù)
NSLog(@"信號(hào)被訂閱");
// 3.發(fā)送數(shù)據(jù)
[subscriber sendNext:@1];
return nil;
}];
經(jīng)過觀察我們不難發(fā)現(xiàn)RACSignal
這個(gè)類后面的Block返回值是RACDisposable
類型的返回值,那么我們就需要返回一個(gè)這樣類型的返回值凳枝!
但是需要注意的是:就算我們不去釋放/銷毀信號(hào)抄沮,那么信號(hào)也會(huì)知道銷毀!
我們試著自己去釋放這個(gè)信號(hào):代碼如下
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// didSubscribe調(diào)用:只要一個(gè)信號(hào)被訂閱就會(huì)調(diào)用
// didSubscribe作用:發(fā)送數(shù)據(jù)
NSLog(@"信號(hào)被訂閱");
// 3.發(fā)送數(shù)據(jù)
[subscriber sendNext:@1];
return [[RACDisposable disposableWithBlock:^{
// block調(diào)用時(shí)刻:當(dāng)信號(hào)發(fā)送完成或者發(fā)送錯(cuò)誤岖瑰,就會(huì)自動(dòng)執(zhí)行這個(gè)block,取消訂閱信號(hào)合是。
// 執(zhí)行完Block后,當(dāng)前信號(hào)就不在被訂閱了锭环。
NSLog(@"信號(hào)被銷毀");
}];];
}];
#######通過RACDisposable 調(diào)用disposableWithBlock進(jìn)行信號(hào)的銷毀看幼!
下面說一下一個(gè)新的類胖眷!RACSubject
這個(gè)類澄惊,以及具體的實(shí)現(xiàn)過程
RACSubject
:信號(hào)提供者:通常會(huì)替換代理狰挡!
特點(diǎn)
:既可以創(chuàng)建信號(hào)彭沼,又可以發(fā)送信號(hào)褪储!
具體使用如下滓走!
第一步:創(chuàng)建信號(hào)权薯!
// 1.創(chuàng)建信號(hào)
RACSubject *subject = [RACSubject subject];//直接通過類方法創(chuàng)建信號(hào)讼呢!
第二部:訂閱信號(hào)撩鹿!
// 不同信號(hào)訂閱的方式不一樣
// RACSubject處理訂閱:僅僅是保存訂閱者
[subject subscribeNext:^(id x) {
NSLog(@"訂閱者一接收到數(shù)據(jù):%@",x);
}];
第三部:發(fā)送信號(hào)!
// 3.發(fā)送數(shù)據(jù)
[subject sendNext:@1];// 這里是自己發(fā)送數(shù)據(jù)
具體的實(shí)現(xiàn)過程:
第一步中創(chuàng)建信號(hào)的時(shí)候:RACSubject會(huì)在內(nèi)部創(chuàng)建一個(gè)可變數(shù)組悦屏,用來接收訂閱者节沦,當(dāng)訂閱者訂閱信號(hào)的時(shí)候回把訂閱者放到這個(gè)數(shù)組中!
第二部中當(dāng)訂閱者去訂閱信號(hào)的時(shí)候會(huì)把創(chuàng)建的訂閱者存放到第一步創(chuàng)建的數(shù)組中!
第三部中當(dāng)subject發(fā)送數(shù)據(jù)的時(shí)候础爬,會(huì)遍歷第一步中的數(shù)組甫贯,然后取出所有的訂閱者然后去發(fā)送數(shù)據(jù)!
這就是RACSubject這個(gè)類的基本使用!下面會(huì)具體介紹這個(gè)類如何作為代理去進(jìn)行使用看蚜!
下面要介紹的是RACSubject的一個(gè)子類叫搁,RACReplaySubject這個(gè)類(重復(fù)提供信號(hào)類)用法基本和上面差不離!o
第一步:創(chuàng)建信號(hào)!
RACReplaySubject *subject = [RACReplaySubject subject];
第二部:訂閱信號(hào)渴逻!
[subject subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
第三部:發(fā)送信號(hào)疾党!
[subject sendNext:@1];
具體的實(shí)現(xiàn)過程:(跟它的父類相比,這個(gè)類比父類更牛13);
第一步中創(chuàng)建信號(hào)的時(shí)候:RACSubject會(huì)在內(nèi)部創(chuàng)建一個(gè)可變數(shù)組惨奕,用來接收訂閱者雪位,當(dāng)訂閱者訂閱信號(hào)的時(shí)候回把訂閱者放到這個(gè)數(shù)組中!
(和父類沒什么兩樣)
第二部中當(dāng)訂閱者去訂閱信號(hào)的時(shí)候會(huì)把要發(fā)送的值存放到第一步創(chuàng)建的數(shù)組中!,并且在這個(gè)一步就開始遍歷數(shù)組中所有的值墓贿!
第三部中當(dāng)subject發(fā)送數(shù)據(jù)的時(shí)候茧泪,會(huì)遍歷第一步中的數(shù)組,然后取出所有的訂閱者然后去發(fā)送數(shù)據(jù)!
這個(gè)和它父類唯一一個(gè)不一樣的地方在于 這個(gè)類可以在信號(hào)被訂閱之前發(fā)送數(shù)據(jù)聋袋,兒RACSubject 卻不行队伟,這就是區(qū)別!幽勒!
下面來說一下這個(gè)RACSubject如何替換代理嗜侮!
拿一個(gè)簡答的例子來說吧!
我們自己自定義了一個(gè)View啥容,View上添加了一個(gè)Button,然后我們需要點(diǎn)擊按鈕通知控制器去做一些事情锈颗!
如果是原本的OC方法
,我么需要寫一整套的代理協(xié)議
咪惠,如果使用RACSubject
這個(gè)類的話击吱,我們只需要在Button的點(diǎn)擊方法中創(chuàng)建一個(gè)信號(hào)
,然后在控制器中去訂閱這個(gè)信號(hào)
就OK了遥昧,就是這個(gè)簡單覆醇!
具體代碼如下
// 需求:
// 1.給當(dāng)前控制器添加一個(gè)按鈕,modal到另一個(gè)控制器界面
// 2.另一個(gè)控制器view中有個(gè)按鈕炭臭,點(diǎn)擊按鈕永脓,通知當(dāng)前控制器
步驟一:在第二個(gè)控制器.h,添加一個(gè)RACSubject代替代理鞋仍。
@interface TwoViewController : UIViewController
@property (nonatomic, strong) RACSubject *delegateSignal;
@end
步驟二:監(jiān)聽第二個(gè)控制器按鈕點(diǎn)擊
@implementation TwoViewController
- (IBAction)notice:(id)sender {
// 通知第一個(gè)控制器常摧,告訴它,按鈕被點(diǎn)了
// 通知代理
// 判斷代理信號(hào)是否有值
if (self.delegateSignal) {
// 有值威创,才需要通知
[self.delegateSignal sendNext:nil];
}
}
@end
步驟三:在第一個(gè)控制器中落午,監(jiān)聽跳轉(zhuǎn)按鈕,給第二個(gè)控制器的代理信號(hào)賦值那婉,并且監(jiān)聽.
@implementation OneViewController
- (IBAction)btnClick:(id)sender {
// 創(chuàng)建第二個(gè)控制器
TwoViewController *twoVc = [[TwoViewController alloc] init];
// 設(shè)置代理信號(hào)
twoVc.delegateSignal = [RACSubject subject];
// 訂閱代理信號(hào)
[twoVc.delegateSignal subscribeNext:^(id x) {
NSLog(@"點(diǎn)擊了通知按鈕");
}];
// 跳轉(zhuǎn)到第二個(gè)控制器
[self presentViewController:twoVc animated:YES completion:nil];
}
@end
下面介紹一下RAC中的集合類板甘!
首先需要了解的是RACTuple這個(gè)類!這個(gè)叫做元組
详炬,但不是Swift
中的元組
,因?yàn)镾wift中的元組可以存放基本數(shù)據(jù)類型
盐类,而RACTuple``不可以存放基本數(shù)據(jù)類型
寞奸,只能存放OC對(duì)象
,如果要存放基本數(shù)據(jù)類型需要進(jìn)行包裝
下面主要介紹的是如何對(duì)RACTuple進(jìn)行遍歷(數(shù)組)
廢話不多說在跳,上代碼
主要思想就是枪萄,把數(shù)組轉(zhuǎn)換為一個(gè)信號(hào),然后去訂閱這個(gè)信號(hào)猫妙,去執(zhí)行一些操作瓷翻!
RACSequence :就是RAC中的集合了,相當(dāng)于OC中的數(shù)組和字典割坠!齐帚,這是個(gè)信號(hào)類
//創(chuàng)建一個(gè)數(shù)組
NSArray *arr = @[@"213",@"321",@1];
// 轉(zhuǎn)換成RAC集合
// RACSequence *sequence = arr.rac_sequence;
// 訂閱集合信號(hào),內(nèi)部會(huì)自動(dòng)遍歷所有的元素發(fā)出來
// [signal subscribeNext:^(id x) {
// NSLog(@"%@",x);
// }];
// 上面是具體的方法。
下面是直接通過鏈?zhǔn)骄幊讨苯泳涂梢猿鰜?[arr.rac_sequence.signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
下面是字典的遍歷
直接上代碼
這里需要解釋一下:RACTupleUnpack 的含義彼哼,這是一個(gè)宏对妄,定義的非常深,確實(shí)是沒有看明白敢朱,但是也不妨礙我們?nèi)ナ褂茫?RACTupleUnpack:用來解析元組剪菱,宏里面的參數(shù),傳需要解析出來的變量名,= 右邊,放需要解析的元組
// 字典
NSDictionary *dict = @{@"account":@"aaa",@"name":@"xmg",@"age":@18};
// 轉(zhuǎn)換成集合
[dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
// NSString *key = x[0];
// NSString *value = x[1];
// NSLog(@"%@ %@",key,value);
// RACTupleUnpack:用來解析元組
// 宏里面的參數(shù),傳需要解析出來的變量名
// = 右邊,放需要解析的元組
RACTupleUnpack(NSString *key,NSString *value) = x;
NSLog(@"%@ %@",key,value);
}];
上面的都是鋪墊拴签,下面介紹一個(gè)比較重要的孝常,也是我們比較關(guān)心的———字典轉(zhuǎn)模型,通過剛剛的方法去進(jìn)行字典轉(zhuǎn)模型
下面把一些主要的代碼寫一下蚓哩!
.h中的代碼
首先我們需要构灸,一個(gè)Plist文件或者是JSON文件
首先我們創(chuàng)建一個(gè)模型,創(chuàng)建兩個(gè)屬性
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *icon;
//然后來個(gè)類方法岸梨!
+ (instancetype)flagWithDict:(NSDictionary *)dict;
.m中的代碼
//通過KVC進(jìn)行賦值
+ (instancetype)flagWithDict:(NSDictionary *)dict
{
Flag *flag = [[self alloc] init];
[flag setValuesForKeysWithDictionary:dict];
return flag;
}
下面是Controller中的代碼
//下面以Plist文件為準(zhǔn)
// 解析plist文件
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath]; NSMutableArray *arr = [NSMutableArray array];
[dictArr.rac_sequence.signal subscribeNext:^(NSDictionary *x) {
Flag *flag = [Flag flagWithDict:x];
[arr addObject:flag];
}];//上面是我們預(yù)想的方法冻押,雖然很方便,但是還是不夠牛13;
下面介紹一種比較叼的方法
// 高級(jí)用法
// 會(huì)把集合中所有元素都映射成一個(gè)新的對(duì)象
NSArray *arr = [[dictArr.rac_sequence map:^id(NSDictionary *value) {
// value:集合中元素
// id:返回對(duì)象就是映射的值
return [Flag flagWithDict:value];
}] array];
NSLog(@"%@",arr);
//面試的時(shí)候可以深深的裝個(gè)13.
下面我們接著介紹盛嘿,RAC中的一些常用的方法!
RAC中另一種方法去代替delegate
代碼如下:
詳解:這個(gè)方法是用來監(jiān)聽某個(gè)方法實(shí)發(fā)觸發(fā)的@ㄌ弧4握住!
2.代替代理
// 1.代替代理:1.RACSubject 2.rac_signalForSelector
// 只要傳值,就必須使用RACSubject
[[_redView rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) {
NSLog(@"控制器知道按鈕被點(diǎn)擊");
}];
// RAC:
// 把控制器調(diào)用didReceiveMemoryWarning轉(zhuǎn)換成信號(hào)
// rac_signalForSelector:監(jiān)聽某對(duì)象有沒有調(diào)用某方法
// [[self rac_signalForSelector:@selector(didReceiveMemoryWarning)] subscribeNext:^(id x) {
// NSLog(@"控制器調(diào)用didReceiveMemoryWarning");
// }];
2.代替KVO
去監(jiān)測一個(gè)View的Frame是否改變
[_redView rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
//
}];
//如果我們要在一個(gè)Controller中監(jiān)聽多個(gè)值得改變的情況下我們可以直接日如下寫锹锰!
[[_redView rac_valuesForKeyPath:@"frame" observer:nil] subscribeNext:^(id x) {
// x:修改的值
NSLog(@"%@",x);
}];
[_redView rac_observeKeyPath:@"bounds" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
//
}];
3.監(jiān)聽事件
[[_btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"按鈕點(diǎn)擊了");
}];
4.代替通知
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
5.監(jiān)聽文本框
[_textField.rac_textSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];