此文章是ReactiveCocoa學習筆記的第二篇携添,未閱讀第一篇的童鞋,請先查閱第一篇ReactiveCocoa學習筆記整理(一) 闹蒜。好的芽卿,廢話不多說揭芍,上一篇中我們簡單的了解了RAC的概念,編程思想以及RACSignal這個基類的簡單應用卸例。我們接著上一篇的內(nèi)容繼續(xù)探尋RAC學習之路称杨。
三. RACSubject基礎知識點
首先,我們介紹另一個使用非常頻繁的類筷转,RACSubject姑原,我們將會從它的概念,使用步驟呜舒,底層實現(xiàn)入手锭汛,慢慢剖析,然后通過幾個簡單的小例子應用一下下袭蝗。
1. RACSubject基本概念以及使用步驟
RACSubject被稱為信號提供者店乐,它既可以自己充當信號,又能夠發(fā)送信號呻袭。最常用的使用場景就是用來代替代理,有了它腺兴,就沒必要定義代理了左电。接下來,我們看一下RACSubject的具體使用步驟:
- 創(chuàng)建信號 [RACSubject subject]页响,跟RACSiganl不一樣篓足,創(chuàng)建信號時沒有block。
- 訂閱信號 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
- 發(fā)送信號 sendNext:(id)value
2. RACSubject底層實現(xiàn)以及簡單應用
RACSubject的底層實現(xiàn)和RACSignal不一樣闰蚕,簡單的介紹一下RACSubject的實現(xiàn)步驟栈拖。
- 調(diào)用subscribeNext訂閱信號,只是把訂閱者保存起來没陡,并且訂閱者的nextBlock已經(jīng)賦值了
- 調(diào)用sendNext發(fā)送信號涩哟,遍歷剛剛保存的所有訂閱者,一個一個調(diào)用訂閱者的nextBlock
在這篇文章就不展開描述了盼玄,有興趣的童鞋可以查閱ReactiveCocoa深入理解 贴彼,里面有關于底層實現(xiàn)的詳細剖析。這里需要對持有訂閱者解釋一下埃儿,由于RACSubject是熱信號, 為了保證未來有事件發(fā)生的時候, 訂閱者可以收到信息, 所以需要持有訂閱者器仗。好了,我們用簡單的小實例探究一下,請看如下代碼:
RACSubject *subject=[RACSubject subject];
[subject subscribeNext:^(id x) {
NSLog(@"訂閱者1的值%@",x);
}];
[subject sendNext:@"wujunyang"];
[subject subscribeNext:^(id x) {
NSLog(@"訂閱者2的值%@",x);
}];
[subject sendNext:@"cnblogs"];
// 輸出:
// 訂閱者1的值wujunyang
// 訂閱者1的值cnblogs
// 訂閱者2的值cnblogs
通過以后的代碼輸出精钮,我們可以很明確的得到結論:RACSubject 發(fā)送過了sendNext威鹿, 下面再去監(jiān)聽它是沒有效果,即RACSubject是熱信號的本質轨香。
上面也提到了忽你,RACSubject最常用的使用場景就是用來代替代理,接下來我們寫一個小的需求來看一下是如何實現(xiàn)的代替代理弹沽。目前檀夹,我們有這樣一個需求:
- 給當前控制器添加一個按鈕,modal到另一個控制器界面
- 另一個控制器view中有個按鈕策橘,點擊按鈕炸渡,通知當前控制器
我們用RACSubject可以這樣來實現(xiàn):
@interface TwoViewController : UIViewController
@property (nonatomic, strong) RACSubject *delegateSignal;
@end
@implementation TwoViewController
- (IBAction)notice:(id)sender {
// 通知第一個控制器,告訴它丽已,按鈕被點了
// 通知代理
// 判斷代理信號是否有值
if (self.delegateSignal) {
// 有值蚌堵,才需要通知
[self.delegateSignal sendNext:nil];
}
}
@end
@implementation OneViewController
- (IBAction)btnClick:(id)sender {
// 創(chuàng)建第二個控制器
TwoViewController *twoVc = [[TwoViewController alloc] init];
// 設置代理信號
twoVc.delegateSignal = [RACSubject subject];
// 訂閱代理信號
[twoVc.delegateSignal subscribeNext:^(id x) {
NSLog(@"點擊了通知按鈕");
}];
// 跳轉到第二個控制器
[self presentViewController:twoVc animated:YES completion:nil];
}
@end
在上述代碼中,大致分為三個步驟:
- 在第二個控制器.h沛婴,添加一個RACSubject代替代理
- 監(jiān)聽第二個控制器按鈕點擊
- 在第一個控制器中吼畏,監(jiān)聽跳轉按鈕,給第二個控制器的代理信號賦值,并且監(jiān)聽
你看蚜点,用RACSubject代替代理就是這么簡單滴就實現(xiàn)啦溃睹。
3. RACReplaySubject簡單介紹以及應用
既然講到了RACSubject,那么久一起把它的子類RACReplaySubject順便說了吧... RACReplaySubject是重復提供信號類性雄,它的使用場景一般有以下兩個:
- 如果一個信號每被訂閱一次,就需要把之前的值重復發(fā)送一遍羹奉,使用重復提供信號類
- 可以設置 capacity 數(shù)量來限制緩存的 value 的數(shù)量,即只緩充最新的幾個值
RACReplaySubject的創(chuàng)建方法也很簡單明了秒旋,首先創(chuàng)建RACReplaySubject,然后訂閱信號诀拭,最后發(fā)送信號迁筛。而它的工作流程可以大致分為以下的三步:
- 訂閱信號時,內(nèi)部保存了訂閱者耕挨,和訂閱者響應block
- 當發(fā)送信號時细卧,遍歷訂閱者,調(diào)用訂閱者的nextBlock
- 發(fā)送的信號會保存起來筒占,當訂閱者訂閱信號時酒甸,會將之前保存的信號,一個一個遍歷
最后赋铝,我們通過簡單的小例子來看一下它的使用:
RACReplaySubject *replaySubject = [RACReplaySubject subject];
[replaySubject subscribeNext:^(id x) {
NSLog(@"1 %@,type:%@",x,NSStringFromClass(object_getClass(x)));
}];
[replaySubject subscribeNext:^(id x) {
NSLog(@"1 %@,type:%@",x,NSStringFromClass(object_getClass(x)));
}];
[replaySubject sendNext:@1];
[replaySubject subscribeNext:^(id x) {
NSLog(@"3 %@,type:%@",x,NSStringFromClass(object_getClass(x)));
}];
//輸出
// 1 1,type:__NSCFNumber
// 1 1,type:__NSCFNumber
// 3 1,type:__NSCFNumber
通過上面的代碼插勤,我們可以看出RACReplaySubject與RACSubject的區(qū)別,RACSubject必須要先訂閱信號之后才能發(fā)送信號,而RACReplaySubject可以先發(fā)送信號后訂閱农尖。
四. RACSequence以及RACTuple的基本簡介跟使用
啊哈哈析恋,在本章節(jié)我們將隆重的介紹RACSequence,它是RAC中的集合類盛卡,可以用于代替NSArray,NSDictionary,可以使用它來快速遍歷數(shù)組和字典助隧。我們就對數(shù)組的操作簡單的總結一下它的流程:
- 把數(shù)組轉換成集合RACSequence numbers.rac_sequence
- 把集合RACSequence轉換RACSignal信號類 numbers.rac_sequence.signal
- 訂閱信號,激活信號滑沧,會自動把集合中的所有值并村,遍歷出來
NSArray *arr = @[@1,@2,@3,@4,@5,@6];
[arr.rac_sequence.signal subscribeNext:^(id x) {
NSLog(@"當前的值x:%@",x);
}];
//輸出
// 當前的值x:1
// 當前的值x:2
// 當前的值x:3
// 當前的值x:4
// 當前的值x:5
// 當前的值x:6
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"jtd",@"name",@"man",@"sex",@"jx",@"jg", nil];
[dict.rac_sequence.signal subscribeNext:^(id x) {
RACTupleUnpack(NSString *key,NSString *value) = x;
NSLog(@"key:%@,value:%@",key,value);
}];
//輸出:
// key:name,value:jtd
// key:sex,value:man
// key:jg,value:jx
如以上代碼所示,就如此方便快捷的實現(xiàn)了對數(shù)組以及字典的遍歷操作滓技。同樣的也可以對轉化之后的數(shù)組進行諸如map,filter,reduce,skip,take,contact..等等操作哩牍,接下來,我們通過代碼來詳細看一下:
NSArray *array=@[@(2),@(5),@(7),@(15)];
RACSequence *sequence = [array rac_sequence];
id mapData = [sequence map:^id(id value) {
return @([value integerValue] * 2);
}];
NSLog(@"序列Map之后的數(shù)據(jù):%@",[mapData array]);
id filterData = [sequence filter:^BOOL(id value) {
return [value integerValue]%2 == 0;
}];
NSLog(@"序列Filter之后的數(shù)據(jù):%@",[filterData array]);
id reduceData = [sequence foldLeftWithStart:@"" reduce:^id(id accumulator, id value) {
return [accumulator stringByAppendingString:[value stringValue]];
}];
NSLog(@"序列Left-Reduce之后的數(shù)據(jù):%@",reduceData);
id rightReduceData = [sequence foldRightWithStart:@"" reduce:^id(id first, RACSequence *rest) {
return [NSString stringWithFormat:@"%@%@", rest.head, first];
}];
NSLog(@"序列Right-Reduce之后的數(shù)據(jù):%@",rightReduceData);
id skipData = [sequence skip:1];
NSLog(@"序列Skip之后的數(shù)據(jù):%@",[skipData array]);
id takeData = [sequence take:2];
NSLog(@"序列Take之后的數(shù)據(jù):%@",[takeData array]);
id takeUntilData = [sequence takeUntilBlock:^BOOL(id x) {
return [x integerValue] == 7;
}];
NSLog(@"序列TakeUntil之后的數(shù)據(jù):%@",[takeUntilData array]);
NSArray *nextArr = @[@"A",@"B",@"C"];
RACSequence *nextSequence = [nextArr rac_sequence];
id contactData = [sequence concat:nextSequence];
NSLog(@"FlyElephant序列Contact之后的數(shù)據(jù):%@",[contactData array]);
//輸出
// 序列Map之后的數(shù)據(jù):(
// 4,
// 10,
// 14,
// 30
// )
// 序列Filter之后的數(shù)據(jù):(
// 2
// )
// 序列Left-Reduce之后的數(shù)據(jù):25715
// 序列Right-Reduce之后的數(shù)據(jù):15752
// 序列Skip之后的數(shù)據(jù):(
// 5,
// 7,
// 15
// )
// 序列Take之后的數(shù)據(jù):(
// 2,
// 5
// )
// 序列TakeUntil之后的數(shù)據(jù):(
// 2,
// 5
// )
// FlyElephant序列Contact之后的數(shù)據(jù):(
// 2,
// 5,
// 7,
// 15,
// A,
// B,
// C
// )
然后令漂,我們再來看一下RACTuple這個類膝昆,RACTuple是ReactiveCocoa的元組類,首先上圖看一下他的定義叠必。
@interface RACTuple : NSObject <NSCoding, NSCopying, NSFastEnumeration>
@property (nonatomic, readonly) NSUInteger count;
@property (nonatomic, readonly) id first;
@property (nonatomic, readonly) id second;
@property (nonatomic, readonly) id third;
@property (nonatomic, readonly) id fourth;
@property (nonatomic, readonly) id fifth;
@property (nonatomic, readonly) id last;
@property (nonatomic, strong) NSArray *backingArray;
@property (nonatomic, copy, readonly) RACSequence *rac_sequence; // 這個是專門為sequence提供的一個擴展
@end
可以看到荚孵,RACTuple的定義看上去很簡單,底層實質就是一個NSArray纬朝,只不過封裝了一些方法收叶。RACTuple繼承了NSCoding, NSCopying, NSFastEnumeration這三個協(xié)議,具體的協(xié)議就不做過多的解讀了共苛,本文本著 "如何使用" 來進行基礎講解判没,況且RACTuple的方法也不多,總共就6個方法俄讹,3個類方法,3個實例方法绕德。RACTuple有如下的優(yōu)點:
- 可用下標訪問元素 (實現(xiàn)了objectAtIndexedSubscript:方法)
- 可用for in枚舉(遵循NSFastEnumeration協(xié)議)
- 可跟NSArray互相轉換
- 可轉換為RACSequence
- 可把NSNull.null轉為RACTupleNil.tupleNil
我們通過簡單的實例來看一下用法患膛,請看如下代碼:
//普通創(chuàng)建
RACTuple *tuple1 = [RACTuple tupleWithObjects:@1, @2, @3, nil];
RACTuple *tuple2 = [RACTuple tupleWithObjectsFromArray:@[@1, @2, @3]];
RACTuple *tuple3 = [[RACTuple alloc] init];
//宏創(chuàng)建
RACTuple *tuple4 = RACTuplePack(@1, @2, @3, @4);
//解包(等號前面是參數(shù)定義,后面是已存在的Tuple耻蛇,參數(shù)個數(shù)需要跟Tuple元素相同)
RACTupleUnpack(NSNumber * value1, NSNumber * value2, NSNumber * value3, NSNumber * value4) = tuple4;
NSLog(@"%@ %@ %@ %@", value1, value2, value3, value4);
//元素訪問方式
NSLog(@"%@", [tuple4 objectAtIndex:1]);
NSLog(@"%@", tuple4[1]);
//輸出
//1 2 3 4
//2
//2
好了踪蹬,今天的文章就到此吧,接下來的文章臣咖,我們將要探討一下RACCommand以及簡單的UIKit的實際運用跃捣,下篇文章見。