簡介
ReactiveCocoa(RAC):RAC是一套基于Cocoa的FRP(Functional Reactive Programming:函數(shù)響應(yīng)式編程)框架。
安裝:可通過CocoaPods: pod "ReactiveObjC"
思想
學(xué)習(xí)一種新的編程范式,困難不在于一門編程語言,而是怎么學(xué)會用另一種方式去思考。思路比語法更重要。
FRP(Functional Reactive Programming)函數(shù)響應(yīng)式編程,這是RAC的核心思想。FRP是一種編程范式,是組合了函數(shù)式編程和響應(yīng)式編程范式。接下來熟悉一下這兩個編程范式。
函數(shù)式編程
如何理解?
如果說面向?qū)ο笫菍?shù)據(jù)的抽象,那么函數(shù)式編程就是對行為的抽象。
就是允許把函數(shù)本身作為參數(shù)傳入另一個函數(shù),還允許返回一個函數(shù)!它的核心是函數(shù)。在解決問題時,使用不可變值和函數(shù),函數(shù)對一個值進行處理,映射成另一個值。
關(guān)鍵點: 參數(shù)是個函數(shù) 返回值也是函數(shù)
現(xiàn)實生活中行為:
洗車:可以將函數(shù)比作一個自動洗車間,臟的汽車進去,自動洗完后,出來還是汽車,只是汽車變干凈了。
響應(yīng)式編程
如何理解?
面向數(shù)據(jù)流的編程思想,類似觀察者模式一樣的表現(xiàn)。
計算是前后呼應(yīng)的,相互關(guān)聯(lián),有些變化了,彼此之間的關(guān)系也會隨著值的變化而變化。
現(xiàn)實生活中行為:
防火:室內(nèi)煙霧散發(fā)到一定程度時,煙感探測器探測到安全極限,及時發(fā)出失火警報。
關(guān)鍵點: 相互關(guān)聯(lián)、跟隨變化
開始學(xué)習(xí)RAC
RAC中Signal的基本的訂閱過程
創(chuàng)建信號
訂閱信號
發(fā)送信號
取消信號
RAC常用的類
RACSignal
RACSignal(冷信號)繼承于抽象類RACStream,是RAC的核心類,信號只有被訂閱時才會送出信號值。
冷信號特點:
- 不可變;
- 被動;
創(chuàng)建冷信號
在RACSignal.h中的聲明的createSignal類方法如下:
+ (RACSignal<ValueType> *)createSignal:(RACDisposable * _Nullable (^)(id<RACSubscriber> subscriber))didSubscribe RAC_WARN_UNUSED_RESULT;
createSignal 類方法說明:
- 作用:創(chuàng)建新的信號
- 參數(shù):didSubscribe,是一個block類型變量,didSubscribe block變量自身是一個類似C語言的函數(shù)指針變量,didSubscribe 指向的函數(shù)包含一個RACSubscriber 參數(shù),返回一個RACDisposable 對象。
- 返回值:返回一個RACSignal類型的信號。
例:
//創(chuàng)建信號
RACSignal *singalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
//發(fā)送信號
[subscriber sendNext:@"1"];
[subscriber sendNext:@(2)];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"------this is RACDisposable---------");
}];
}];
//訂閱信號
[singalA subscribeNext:^(id _Nullable x) {
NSLog(@"-------singalA value is %@-------",x);
}];
返回結(jié)果如下:
2021-04-10 22:10:30.127793+0800 RACDemo[14148:238689] -------singalA value is 1-------
2021-04-10 22:10:30.127938+0800 RACDemo[14148:238689] -------singalA value is 2-------
2021-04-10 22:10:30.128098+0800 RACDemo[14148:238689] ------this is RACDisposable---------
上面的過程拆分一下可以這樣寫:
//聲明一名稱為didSubscribe的block變量,帶有一個RACSubscriber類型參數(shù),返回一個RACDisposable類型對象
RACDisposable * (^didSubscribe)(id<RACSubscriber> subscriber);
//為didSubscribe定義,(block的實現(xiàn))
didSubscribe = ^RACDisposable * (id<RACSubscriber> sub){
//發(fā)送信號
[sub sendNext:@"1"];
[sub sendNext:@(2)];
[sub sendCompleted];
[sub sendError:nil];
return [RACDisposable disposableWithBlock:^{
NSLog(@"----didSubscribe--RACDisposable--------");
}];
};
//創(chuàng)建信號
RACSignal *signalB = [RACSignal createSignal:didSubscribe];
//訂閱信號
[signalB subscribeNext:^(id _Nullable x) {
NSLog(@"------signalB x is %@--------",x);
}];
返回結(jié)果如下:
2021-04-10 22:13:58.758873+0800 RACDemo[14258:241973] ------signalB x is 1--------
2021-04-10 22:13:58.758993+0800 RACDemo[14258:241973] ------signalB x is 2--------
2021-04-10 22:13:58.759078+0800 RACDemo[14258:241973] ----didSubscribe--RACDisposable-------
RACSubscriber
訂閱者:負責發(fā)信號,本身是個協(xié)議;只要遵循它,并且實現(xiàn)協(xié)議內(nèi)的方法,就會成為訂閱者。
常用的幾個方法:
//發(fā)送值到訂閱者
- (void)sendNext:(nullable id)value;
//發(fā)送錯誤給訂閱者
- (void)sendError:(nullable NSError *)error;
//告訴訂閱者發(fā)送完成了
- (void)sendCompleted;
RACDisposable
取消訂閱或清理資源,發(fā)送失敗或者完成,會自動觸發(fā)取消訂閱方法。
//
+ (instancetype)disposableWithBlock:(void (^)(void))block;
//取消訂閱
- (void)dispose;
RACSubject
RACSubject(熱信號)t繼承于RACSignal,一個subject可認為是一個信號,你可以手動控制sendNext、sendError、sendCompleted事件??梢詭椭銓⒎荝AC橋接到RAC上。
熱信號特點:
可變;
主動;
可以橋接RAC和非RAC;
RACSubject 對外公開的接口只有兩個,一個是創(chuàng)建一個熱信號對象,另一個是發(fā)送消息的方法。
/// Returns a new subject.
+ (instancetype)subject;
// Redeclaration of the RACSubscriber method. Made in order to specify a generic type.
- (void)sendNext:(nullable ValueType)value;
創(chuàng)建熱信號的接口:
+ (instancetype)subject;
這個方法創(chuàng)建的時候,會初始化兩個私有屬性,一個是subscribers數(shù)組 、另一個是disposable。
subscribers:數(shù)組里包含了所有的當前訂閱者,在self的同步操作時候會使用它遍歷所屬的訂閱者。
disposable:包含接收者對其他信號的所有訂閱。主要是用于取消訂閱和清理資源的操作。
發(fā)送信號的接口:
- (void)sendNext:(nullable ValueType)value;
重新定義了RACSubscriber協(xié)議的sendNext方法。
創(chuàng)建熱信號
例:創(chuàng)建一個熱信號
//創(chuàng)建信號
RACSubject *subject=[RACSubject subject];
//訂閱信號
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"subject x is [%@]",x);
}];
//發(fā)送信號
[subject sendNext:@"123"];
//結(jié)束訂閱
[subject sendCompleted];
返回結(jié)果如下:
2021-04-13 16:48:52.043076+0800 RACDemo[29017:264320] subject x is [123]
常用高階函數(shù)
綁定 bind
作用: 可將信號攔截后進行過濾再發(fā)送給新的信號,類似消息轉(zhuǎn)發(fā)
typedef RACSignal * _Nullable (^RACSignalBindBlock)(ValueType _Nullable value, BOOL *stop);
- (RACSignal *)bind:(RACSignalBindBlock (^)(void))block RAC_WARN_UNUSED_RESULT;
例:
// //創(chuàng)建信號
RACSubject *subject = [RACSubject subject];
//綁定新的信號
RACSignal *singnal=[subject bind:^RACSignalBindBlock _Nonnull {
return ^RACSignal*(id value,BOOL *stop){
return[RACReturnSignal return:value];
};
}];
// 訂閱信號
[singnal subscribeNext:^(id _Nullable x) {
NSLog(@"bind x is [%@]",x);
}];
//發(fā)送信號
[subject sendNext:@"send bind value 123"];
返回結(jié)果如下:
2021-04-12 10:16:57.562421+0800 RACDemo[4075:59403] bind x is [send bind value 123]
合并
concat
作用:按順序合并多個信號為一個信號,其中前一個信號必須發(fā)送sendCompleted表示執(zhí)行完成,才會執(zhí)行后面一個信號,類似串行隊列,先完成一個請求,才進行下一個請求。
- (RACSignal *)concat:(RACSignal *)signal RAC_WARN_UNUSED_RESULT;
例:
//創(chuàng)建信號
RACSubject *subject=[RACSubject subject];
//創(chuàng)建信號
RACSubject *subject2=[RACSubject subject];
//連接信號
RACSignal *signal=[subject concat:subject2];
//訂閱信號
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"concat x is [%@]",x);
}];
//發(fā)送信號
[subject sendNext:@"abc"];
//subject 信號執(zhí)行完成,必須加上 sendCompleted,表示信號結(jié)束了,否則無法連接下一個信號
[subject sendCompleted];
//subject2發(fā)送信號
[subject2 sendNext:@"ABC"];
返回結(jié)果如下:
2021-04-12 16:42:14.281303+0800 RACDemo[17888:262262] concat x is [abc]
2021-04-12 16:42:14.281532+0800 RACDemo[17888:262262] concat x is [ABC]
merge
作用:按順序合并多個信號為一個信號,其中前一個信號不添加sendCompleted,也會執(zhí)行后面一個信號。只要有一個信號來,合并操作就會接受。不強調(diào)先完成一個再進行下一個。
例:
//創(chuàng)建信號
RACSubject *subject=[RACSubject subject];
//創(chuàng)建信號
RACSubject *subject2=[RACSubject subject];
//連接信號
RACSignal *signal=[subject merge:subject2];
//訂閱信號
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"merge x is [%@]",x);
}];
//發(fā)送信號
[subject sendNext:@"abc"];
//subject2發(fā)送信號
[subject2 sendNext:@"ABC"];
返回結(jié)果如下:
2021-04-12 17:08:23.410822+0800 RACDemo[19726:287122] merge x is [abc]
2021-04-12 17:08:23.411005+0800 RACDemo[19726:287122] merge x is [ABC]
combineLatest
作用:按順序合并多個信號的最新值為元組
例:
//創(chuàng)建信號
RACSubject *subject = [RACSubject subject];
//創(chuàng)建信號
RACSubject *subject2 = [RACSubject subject];
RACSignal *signal = [RACSignal combineLatest: @[subject, subject2]];
//訂閱信號
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"combineLatest x is [%@]",x);
}];
//發(fā)送信號
[subject sendNext:@"a"];
//發(fā)送信號
[subject2 sendNext:@"b"];
//發(fā)送信號
[subject2 sendNext:@"A"];
//發(fā)送信號
[subject sendNext:@"B"];
返回結(jié)果如下:
2021-04-12 17:27:35.030304+0800 RACDemo[20891:303336] combineLatest x is [<RACTuple: 0x600003af40b0> (
a,
b
)]
2021-04-12 17:27:35.030604+0800 RACDemo[20891:303336] combineLatest x is [<RACTuple: 0x600003ae4ee0> (
a,
A
)]
2021-04-12 17:27:35.030805+0800 RACDemo[20891:303336] combineLatest x is [<RACTuple: 0x600003ae85c0> (
B,
A
)]
映射
flattenMap
作用: 用于信號中信號,把源信號的內(nèi)容映射成一個新的信號,信號是RACSignal類型
- (RACSignal *)flattenMap:(__kindof RACSignal * _Nullable (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;
例:映射值后,返回一下新的信號
// //創(chuàng)建信號
RACSubject *subject = [RACSubject subject];
// 綁定信號,并返回映射后的值
RACSignal * flattenMapSignal =[subject flattenMap:^__kindof RACSignal * _Nullable(id _Nullable value) {
value=[NSString stringWithFormat:@"flattenMapSignal映射后的值為:%@",value];
return [RACReturnSignal return:value];
}];
//訂閱信號
[flattenMapSignal subscribeNext:^(id _Nullable x) {
NSLog(@"flattenMapSignal x is [%@]",x);
}];
//發(fā)送信號
[subject sendNext:@"1234"];
返回結(jié)果如下:
2021-04-11 08:30:20.425283+0800 RACDemo[5943:81326] flattenMapSignal x is [flattenMapSignal映射后的值為:1234]
map
作用: 把源信號的值映射成一個新的值,可以是任意類型的
- (RACSignal *)map:(id _Nullable (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;
例:返回加工后的字符串
//創(chuàng)建信號
RACSubject *subject = [RACSubject subject];
//綁定信號
RACSignal *bindSignal = [subject map:^id _Nullable(id _Nullable value) {
//信號發(fā)送的內(nèi)容
NSString *str = [NSString stringWithFormat:@"映射 map 處理后的數(shù)據(jù):%@", value];
//返回任意類型的值
return str;
}];
//訂閱信號
[bindSignal subscribeNext:^(id x) {
NSLog(@"map x is [%@]",x);
}];
//發(fā)送數(shù)據(jù)
[subject sendNext:@1];
返回結(jié)果如下:
2021-04-11 08:34:34.676217+0800 RACDemo[6105:85742] map x is [映射 map 處理后的數(shù)據(jù):1]
例:返回一個新的信號
RACSubject *subject2=[RACSubject subject];
//綁定信號
RACSignal * bindSignal2=[subject2 map:^RACSignal * _Nullable(id _Nullable value) {
//映射后,返回一個新的信號對象
value=[NSString stringWithFormat:@"修改value %@",value];
return [RACReturnSignal return:value];
}];
//訂閱信號
[bindSignal2 subscribeNext:^(id _Nullable x) {
NSLog(@"返回值是信號 [%@]",x);
}];
//發(fā)送信號
[subject2 sendNext:@(2)];
返回結(jié)果如下:
2021-04-11 08:41:03.927420+0800 RACDemo[6274:90310] 返回值是信號 [<RACReturnSignal: 0x600001e9d400> name: ]
過濾
filter
作用: 通過條件判斷,過濾掉相應(yīng)的信號
- (RACSignal<ValueType> *)filter:(BOOL (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;
例:過濾掉ABC開頭的字符串
//創(chuàng)建信號
RACSubject *subject=[RACSubject subject];
//過濾掉ABC開頭的字符串
RACSignal *signal= [subject filter:^BOOL(id _Nullable value) {
NSString *str=value;
if ([str hasPrefix:@"ABC"]) {
return NO;
}else{
return YES;
}
}];
//訂閱信號
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"過濾后的結(jié)果為 [%@]",x);
}];
//發(fā)送信號
[subject sendNext:@"abc123"];
//發(fā)送信號
[subject sendNext:@"ABC123"];
返回結(jié)果如下:
2021-04-11 10:44:03.423107+0800 RACDemo[10384:185744] 過濾后的結(jié)果為 [abc123]
ignore
作用: 過濾信號值,忽略掉值為xxx的信號,按值忽略
- (RACSignal<ValueType> *)ignore:(nullable ValueType)value RAC_WARN_UNUSED_RESULT;
例:忽略掉值為ABC123的信號
//創(chuàng)建信號
RACSubject* subject=[RACSubject subject];
//忽略掉值為ABC123的信號
RACSignal *signal=[subject ignore:@"ABC123"];
//訂閱信號
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"ignore x is [%@]",x);
}];
//發(fā)送信號
[subject sendNext:@"ABC123"];
//發(fā)送信號
[subject sendNext:@"abc123"];
返回結(jié)果如下:
2021-04-11 10:37:35.490486+0800 RACDemo[10205:180228] ignore x is [abc123]
then
- (RACSignal *)then:(RACSignal * (^)(void))block RAC_WARN_UNUSED_RESULT;
作用:忽略掉前一個信號發(fā)送的值,必須等前一個信號發(fā)送sendCompleted表示執(zhí)行完成后,才執(zhí)行下一個信號,否則無效,按信號忽略
例:忽略掉subject 信號,然后再執(zhí)行subject2信號
//創(chuàng)建信號
RACSubject *subject = [RACSubject subject];
//創(chuàng)建信號
RACSubject *subject2 = [RACSubject subject];
//忽略掉subject 信號
RACSignal *signal = [subject then:^RACSignal * _Nonnull{
return subject2;
}];
//訂閱信號
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"then x is [%@]",x);
}];
//發(fā)送信號
[subject sendNext:@"a"];
// 必須添加sendCompleted ,表示subject 信號執(zhí)行完成,否則無效
[subject sendCompleted];
//發(fā)送信號
[subject2 sendNext:@"b"];
//發(fā)送信號
[subject2 sendNext:@"A"];
返回結(jié)果如下:
2021-04-12 17:45:59.354229+0800 RACDemo[22106:321645] then x is [b]
2021-04-12 17:45:59.354343+0800 RACDemo[22106:321645] then x is [A]
take
作用: 按順序獲取第1次到n次信號
- (RACSignal<ValueType> *)take:(NSUInteger)count RAC_WARN_UNUSED_RESULT;
例:獲取從第1~4次信號
//創(chuàng)建信號
RACSubject* subject=[RACSubject subject];
//獲取從第1~4次信號,若傳0則不取信號
RACSignal *signal=[subject take:4];
//訂閱信號
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"take x is [%@]",x);
}];
//發(fā)送信號
[subject sendNext:@"ABC123"];
//發(fā)送信號
[subject sendNext:@"123ABC"];
//發(fā)送信號
[subject sendNext:@"abc123"];
//發(fā)送信號
[subject sendNext:@"123abc"];
//發(fā)送信號
[subject sendNext:@"123"];
//發(fā)送信號
[subject sendNext:@"abc"];
返回結(jié)果如下:
2021-04-11 10:59:05.655107+0800 RACDemo[10795:197326] take x is [ABC123]
2021-04-11 10:59:05.655298+0800 RACDemo[10795:197326] take x is [123ABC]
2021-04-11 10:59:05.655436+0800 RACDemo[10795:197326] take x is [abc123]
2021-04-11 10:59:05.655573+0800 RACDemo[10795:197326] take x is [123abc]
skip
作用: 跳過從第1次~n次 的信號
例:跳過從第1~3次信號
//創(chuàng)建信號
RACSubject* subject=[RACSubject subject];
//跳過從第1~3次信號
RACSignal *signal=[subject skip:3];
//訂閱信號
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"skip x is [%@]",x);
}];
//發(fā)送信號
[subject sendNext:@"123"];
//發(fā)送信號
[subject sendNext:@"abc"];
//發(fā)送信號
[subject sendNext:@"ABC"];
//發(fā)送信號
[subject sendNext:@"123abc"];
返回結(jié)果如下:
2021-04-11 11:18:20.866481+0800 RACDemo[11294:209783] skip x is [123abc]
switchToLatest
作用: 信號內(nèi)的信號,獲取信號內(nèi)信號發(fā)送的最新的信號,當發(fā)送多個內(nèi)部信號時候,只接受最新的內(nèi)部信號
例:
//創(chuàng)建信號
RACSubject* subject=[RACSubject subject];
//創(chuàng)建內(nèi)部信號,用于subject內(nèi)部的信號
RACSubject * innerSubject=[RACSubject subject];
//創(chuàng)建內(nèi)部信號1,用于subject內(nèi)部的信號
RACSubject * innerSubject1=[RACSubject subject];
//訂閱信號,訂閱subject內(nèi)的即innerSubject信號
[subject.switchToLatest subscribeNext:^(id _Nullable x) {
NSLog(@"switchToLatest x is [%@]",x);
}];
//subject發(fā)送innerSubject信號
[subject sendNext:innerSubject];
//subject發(fā)送innerSubject1信號
[subject sendNext:innerSubject1];
//通過innerSubject來發(fā)送信號
[innerSubject sendNext:@"abc"];
//通過innerSubject1來發(fā)送信號
[innerSubject1 sendNext:@"123abc"];
返回結(jié)果如下:
2021-04-11 11:41:27.553097+0800 RACDemo[12001:229779] switchToLatest x is [123abc]
distinctUntilChanged
作用: 忽略重復(fù)的信號
例:執(zhí)行后,忽略了重復(fù)的1
//創(chuàng)建信號
RACSubject* subject=[RACSubject subject];
//訂閱信號,只在訂閱的信號與上一次不同時才會響應(yīng)
[subject.distinctUntilChanged subscribeNext:^(id _Nullable x) {
NSLog(@"distinctUntilChanged x is [%@]",x);
}];
//發(fā)送信號
[subject sendNext:@"1"];
//發(fā)送信號
[subject sendNext:@"1"];
//發(fā)送信號
[subject sendNext:@"123"];
返回結(jié)果如下:
2021-04-11 11:53:14.833259+0800 RACDemo[12347:240090] distinctUntilChanged x is [1]
2021-04-11 11:53:14.833524+0800 RACDemo[12347:240090] distinctUntilChanged x is [123]
總結(jié)
RAC的高內(nèi)聚,低耦合特性,很好的配合了MVVM的使用。因此如果現(xiàn)在正在使用MVVM模式開發(fā),不妨試一下RAC框架。 以上是對RAC中的常用類、函數(shù)、宏的一些使用說明,并未對每個功能的底層原理進行分析,主要是介紹RAC是如何應(yīng)用于業(yè)務(wù)中的。學(xué)習(xí)一項新的框架的步驟,一般是先知其所用,而后再知其所以然,后續(xù)再對某些功能進行詳細的分析。