一.編程思想
先簡單介紹下目前咱們已知的編程思想。
1.面向過程:處理事情以過程為核心,一步一步的實現。
C語言是面向過程編程的.
面向過程編程主要使用順序、條件選擇、循環三種基本結構來編寫程序。
1.1 順序:按照時間軸順序完成每個處理;
1.2 條件選擇:根據條件的成立與否執行不同的條件分支;
1.3 循環:根據一定的條件反復執行同樣的代碼;面向過程就是分析出解決問題所需要的步驟,然后用函數把這些步驟一步一步實現,使用的時候一個一個依次調用就可以了。
例如:洗衣機的洗衣功能:放水、洗滌、脫水、打干,按照一定的順序執行功能:放水->洗滌->脫水->放水->洗滌->脫水->打干。
在早期計算機配置低、內存小為了節省內存空間,大都采用面向過程編程(以時間換空間)。
2.面向對象:萬物皆對象
滿足面向對象編程的語言,一般會提供類、封裝、繼承、多態等語法和概念來輔助我們進行面向對象編程。
所謂的面向對象就是將我們的程序模塊化、對象化,把具體事物的特性屬性和通過這些屬性來實現一些動作的具體方法封裝到一個類里面,然后創建實例對象,以對象作為程序的基本模塊來進行軟件的分析、設計和開發的一種思考方法。
3.鏈式編程思想:是將多個操作(多行代碼)通過點號(.)鏈接在一起成為一句代碼,使代碼可讀性好。a(1).b(2).c(3)
鏈式編程特點:方法的返回值是block,block必須有返回值(本身對象),block參數(需要操作的值)
代表:masonry框架。
例子:
TestView.h
Paste_Image.png
使用方法:
Paste_Image.png
4.響應式編程思想:不需要考慮調用順序,只需要知道考慮結果,類似于蝴蝶效應,產生一個事件,會影響很多東西,這些事件像流一樣的傳播出去,然后影響結果,借用面向對象的一句話,萬物皆是流。
代表:KVO運用。4.1 響應式編程(FRP Functional Reactive Programming),為解決現代編程問題提供了全新的視角.一旦理解它,可以極大地簡化你的項目,特別是處理嵌套回調的異步事件,復雜的列表過濾和變換,或者時間相關問題。
4.2 響應式編程是一種新的編程風格,其特點是異步或并發、事件驅動、推送PUSH機制以及觀察者模式的衍生。reactive 應用(響應式應用)允許開發人員構建事件驅動(event-driven),可擴展性,彈性的反應系統:提供高度敏感的實時的用戶體驗感覺,可伸縮性和彈性的應用程序棧的支持,隨時可以部署在多核和云計算架構。
二.ReactiveCocoa初見
簡介
ReactiveCocoa簡稱RAC,是一個iOS和OS中的函數式響應式編程框架。
作用
RAC關鍵解決的問題是開發中經常回見的“低聚合,高耦合”問題。在RAC出現之前,我們編寫iOS代碼,大部分都是在響應一些事件:按鈕點擊、接收網絡消息、屬性變化等等。但處理事件的形式在蘋果官方API中卻有好幾種:如target-action、代理方法、KVO、回調或其它。以上這幾種,往往在一個項目中基本都會使用到,在不同的地方會出現很多處理事件的形式,這就帶來了不能很好統一管理問題。因此,我們想,有沒有一個統一管理的解決方案呢?這個方案又是怎樣的呢?到這里ReactiveCocoa就該粉墨登場了,它出現的目的就是為了解決統一標準去管理代碼中的事件。
思想
1.函數式編程(Functional Programming):使用高階函數,例如函數用其他函數作為參數。
2.響應式編程(Reactive Programming):關注于數據流和變化傳播。所以,你可能聽說過ReactiveCocoa被描述為函數響應式編程(FRP)框架。 以后使用RAC解決問題,就不需要考慮調用順序,直接考慮結果,把每一次操作都寫成一系列嵌套的方法中,使代碼高聚合,方便管理。
原理
1、運用的是Hook(鉤子)思想,Hook是一種用于改變API(應用程序編程接口:方法)執行結果的技術.
2、Hook用處:截獲API調用的技術。
3、Hook原理:在每次調用一個API返回結果之前,先執行你自己的方法,改變結果的輸出。
三.ReactiveCocoa 在objective-c下的簡單使用
目前最新的版本是Swift的 如果想在OC環境下使用只能用 2.5 的版本
Pod 導入
pod 'ReactiveCocoa', '2.5'
1.RACSiganl
RACSiganl:信號類,一般表示將來有數據傳遞,只要有數據改變,信號內部接收到數據,就會馬上發出數據。
信號類(RACSiganl),只是表示當數據改變時,信號內部會發出數據,它本身不具備發送信號的能力,而是交給內部一個訂閱者去發出。
默認一個信號都是冷信號,也就是值改變了,也不會觸發,只有訂閱了這個信號,這個信號才會變為熱信號,值改變了才會觸發。
如何訂閱信號:調用信號RACSignal的subscribeNext就能訂閱。
// RACSignal使用步驟:
// 1.創建信號 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3.發送信號 - (void)sendNext:(id)value
[subscriber sendNext:@"12345"];
// 如果不在發送數據,最好發送信號完成,內部會自動調用[RACDisposable disposable]取消訂閱信號。
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
// block調用時刻:當信號發送完成或者發送錯誤,就會自動執行這個block,取消訂閱信號。
// 執行完Block后,當前信號就不在被訂閱了。
NSLog(@"signal 銷毀");
}];
}];
// 2.訂閱信號,才會激活信號. - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
[signal subscribeNext:^(id x) {
// block調用時刻:每當有信號發出數據,就會調用block.
NSLog(@"接收到消息->%@", x);
}];
2.RACSubject
RACSubject:信號提供者,自己可以充當信號,又能發送信號。
使用場景:通常用來代替代理,有了它,就不必要定義代理了。
// RACSubject使用步驟
// 1.創建信號 [RACSubject subject],跟RACSiganl不一樣,創建信號時沒有block。
RACSubject *subject = [RACSubject subject];
// 2.訂閱信號 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
[subject subscribeNext:^(id x) {
// block調用時刻:當信號發出新值,就會調用.
NSLog(@"第一個訂閱者%@",x);
}];
[subject subscribeNext:^(id x) {
// block調用時刻:當信號發出新值,就會調用.
NSLog(@"第二個訂閱者%@",x);
}];
// 3.發送信號 sendNext:(id)value
[subject sendNext:@"逗比123"];
3.RACReplaySubject
RACReplaySubject:重復提供信號類,RACSubject的子類。
使用場景一: 如果一個信號每被訂閱一次,就需要把之前的值重復發送一遍,使用重復提供信號類。
使用場景二: 可以設置capacity數量來限制緩存的value的數量,即只緩充最新的幾個值。
// RACReplaySubject使用步驟:
// 1.創建信號 [RACSubject subject],跟RACSiganl不一樣,創建信號時沒有block。
RACReplaySubject *replaySubject = [RACReplaySubject subject];
// 2.可以先訂閱信號,也可以先發送信號。
//發送信號
[replaySubject sendNext:@"訂閱之前發送的1"];
// 2.1 訂閱信號 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
[replaySubject subscribeNext:^(id x) {
NSLog(@"第一個訂閱者接收到的數據%@",x);
}];
// 2.2 發送信號 sendNext:(id)value
[replaySubject sendNext:@"訂閱之后發送的2"];
4.RACTuple RACSequence
RACTuple 元組類,類似NSArray,用來包裝值.
RACSequence:RAC中的集合類,用于代替NSArray,NSDictionary,可以使用它來快速遍歷數組和字典。
/**
RACTuple:元組類,類似NSArray,用來包裝值.
*/
/**
RACSequence:RAC中的集合類,用于代替NSArray,NSDictionary,可以使用它來快速遍歷數組和字典。
*/
// 1.遍歷數組
NSArray *strArr = @[@"1", @"2", @"3", @"4", @"5", @"6",@"1", @"2", @"3", @"4", @"5", @"6"];
// 這里其實是三步
// 第一步: 把數組轉換成集合RACSequence numbers.rac_sequence
// 第二步: 把集合RACSequence轉換RACSignal信號類,numbers.rac_sequence.signal
// 第三步: 訂閱信號,激活信號,會自動把集合中的所有值,遍歷出來。
[strArr.rac_sequence.signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 2.遍歷字典,遍歷出來的鍵值對會包裝成RACTuple(元組對象)
NSDictionary *dict = @{@"name":@"xmg",@"age":@18};
[dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
// 解包元組,會把元組的值,按順序給參數里面的變量賦值
RACTupleUnpack(NSString *key, NSString *value) = x;
// 相當于以下寫法
// NSString *key = x[0];
// NSString *value = x[1];
NSLog(@"%@ %@",key,value);
}];
// 3.字典轉模型
NSMutableArray *dictArr = [NSMutableArray array];
for (int i=0; i<10; i++) {
[dictArr addObject:@{@"name":[NSString stringWithFormat:@"張%@", @(i)], @"age":@(i)}];
}
NSArray *objArr = [[dictArr.rac_sequence.signal map:^id(id value) {
return [Person personWithDict:value];
}] toArray];
NSLog(@"->%@", objArr);
5.RACCommand
RAC中用于處理事件的類,可以把事件如何處理,事件中的數據如何傳遞,包裝到這個類中,他可以很方便的監控事件的執行過程。
使用場景:監聽按鈕點擊,網絡請求
// 1.創建命令 initWithSignalBlock:(RACSignal * (^)(id input))signalBlock
// 2.在signalBlock中,創建RACSignal,并且作為signalBlock的返回值
// 3.執行命令 - (RACSignal *)execute:(id)input
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
NSLog(@"執行命令");
// 創建空信號,必須返回信號
// return [RACSignal empty];
// 2.創建信號,用來傳遞數據
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"請求數據789"];
// 注意:數據傳遞完,最好調用sendCompleted,這時命令才執行完畢。
[subscriber sendCompleted];
return nil;
}];
}];
// 強引用命令,不要被銷毀,否則接收不到數據
_conmmand = command;
// 3.訂閱RACCommand中的信號
[command.executionSignals subscribeNext:^(id x) {
[x subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}];
// RAC高級用法
// switchToLatest:用于signal of signals,獲取signal of signals發出的最新信號,也就是可以直接拿到RACCommand中的信號
[command.executionSignals.switchToLatest subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 4.監聽命令是否執行完畢,默認會來一次,可以直接跳過,skip表示跳過第一次信號。
[[command.executing skip:1] subscribeNext:^(id x) {
if ([x boolValue] == YES) {
// 正在執行
NSLog(@"正在執行");
}else{
// 執行完成
NSLog(@"執行完成");
}
}];
// 5.執行命令
[_conmmand execute:@1];
6.RACMulticastConnection
RACMulticastConnection:用于當一個信號,被多次訂閱時,為了保證創建信號時,避免多次調用創建信號中的block,造成副作用,可以使用這個類處理。
使用注意:RACMulticastConnection通過RACSignal的-publish或者-muticast:方法創建.
// 需求:假設在一個信號中發送請求,每次訂閱一次都會發送請求,這樣就會導致多次請求。
//1.創建請求信號
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"發送請求");
[subscriber sendNext:@"我是測試"];
return nil;
}];
//2.訂閱信號
[signal subscribeNext:^(id x) {
NSLog(@"接收數據1->%@", x);
}];
[signal subscribeNext:^(id x) {
NSLog(@"接收數據2->%@", x);
}];
// 3.運行結果,會執行兩遍發送請求,也就是每次訂閱都會發送一次請求
// 解決:使用RACMulticastConnection就能解決.
// RACMulticastConnection:解決重復請求問題
// 1.創建信號
RACSignal *signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"發送請求1");
[subscriber sendNext:@"haha"];
return nil;
}];
//2.創建連接
RACMulticastConnection *multicastConnection = [signal1 publish];
//3.訂閱信號
// 注意:訂閱信號,也不能激活信號,只是保存訂閱者到數組,必須通過連接,當調用連接,就會一次性調用所有訂閱者的sendNext:
[multicastConnection.signal subscribeNext:^(id x) {
NSLog(@"訂閱者1 信號->%@", x);
}];
[multicastConnection.signal subscribeNext:^(id x) {
NSLog(@"訂閱者2 信號->%@", x);
}];
//4.連接,激活信號
[multicastConnection connect];