這個(gè)topic是很久以來(lái)多想寫的霹娄,但是一直沒有被迫寫,所以這次嘗試寫一下~
RAC 指的就是 RactiveCocoa 鲫骗,是 Github 的一個(gè)開源框架犬耻,能夠通過信號(hào)提供大量方便的事件處理方案,讓我們更簡(jiǎn)單粗暴地去處理事件执泰,現(xiàn)在分為 ReactiveObjC(OC) 和 ReactiveSwift(swift)枕磁。
所以使用的話就只要:
pod 'ReactiveObjC'
RAC可以處理事件的監(jiān)聽,接管了蘋果所有的事件機(jī)制(addTarget术吝,代理计济,通知,KVO)
排苍,是一套重量級(jí)響應(yīng)式函數(shù)式編程開源框架沦寂,它可以幫助我們簡(jiǎn)單的處理事件。它具有高聚合低耦合的特性淘衙。但需要注意Block中的循環(huán)引用問題
這里引入了兩個(gè)問題:什么是響應(yīng)式 & 什么是函數(shù)式传藏?
什么是函數(shù)式?
函數(shù)式的一個(gè)典型是swift那種彤守,那么它的定義是什么呢毯侦?
Functional programming is a programming paradigm
1.treats computation as the evaluation of mathematical functions
2.avoids changing-state and mutable data
by wikipedia
從它的定義也可以知道函數(shù)式的主要特點(diǎn):
- 沒有使用外部變量,只是使用了傳入的參數(shù)具垫,類似數(shù)學(xué)中的函數(shù)叫惊,真正的是函數(shù)的輸入映射到輸出,只要固定輸入做修,那么每次執(zhí)行函數(shù)的結(jié)果都是一樣的。
(所以它不怕多線程執(zhí)行)
那么追求純函數(shù)的理由是什么呢:
-
可緩存性:
var squareNumber = memoize(function(x){ return x*x; });
squareNumber(4);//=> 16
squareNumber(4); // 從緩存中讀取輸入值為 4 的結(jié)果 -
可移植性/自文檔化:
純函數(shù)是完全自給自足的抡草,它需要的所有東西都能輕易獲得饰及。自給自足:不使用外部數(shù)據(jù)或者函數(shù),所有函數(shù)內(nèi)部所需要的外部數(shù)據(jù)都以參數(shù)的形式傳入康震。 -
可測(cè)試性:
相同輸入得到相同的輸出 -
合理性(引用透明性):
使用一種叫做“等式推導(dǎo)”(equational reasoning)的技術(shù)來(lái)分析代碼燎含。所謂“等式推導(dǎo)”就是“一對(duì)一”替換,有點(diǎn)像在不考慮程序性執(zhí)行的怪異行為(quirks of programmatic evaluation)的情況下腿短,手動(dòng)執(zhí)行相關(guān)代碼屏箍。 -
并行代碼:
可以并行運(yùn)行任意純函數(shù)绘梦。因?yàn)榧兒瘮?shù)根本不需要訪問共享的內(nèi)存,而且根據(jù)其定義赴魁,純函數(shù)也不會(huì)因副作用而進(jìn)入競(jìng)爭(zhēng)態(tài)
函數(shù)是一等公民(first-class citizens)卸奉。層級(jí)越高,權(quán)力越大颖御。很多時(shí)候高層的人有權(quán)利做的事榄棵,下面的人就不能做。
程序世界里潘拱,有且僅有這么幾種權(quán)力: 創(chuàng)建疹鳄,賦值(賦值給變量),傳遞(當(dāng)作參數(shù)傳遞)芦岂。
不同的編程語(yǔ)言瘪弓,函數(shù)的權(quán)利會(huì)不一樣。
- 在java語(yǔ)言中禽最,創(chuàng)建/賦值/傳遞腺怯,這些權(quán)利object 都具備,function 都不具備弛随。對(duì)象可以通過參數(shù)傳遞到另一個(gè)對(duì)象里瓢喉,從而兩個(gè)對(duì)象可以互相通信。函數(shù)卻不行舀透,兩個(gè)函數(shù)想要通信栓票,必須以對(duì)象為介質(zhì)。
- 在js中愕够,函數(shù)可以創(chuàng)建/賦值/傳遞走贪,所以在js中函數(shù)是一等公民。函數(shù)是 javascript 的主要工作單元惑芭。
Programming Paradigm 編程范式是什么坠狡?
編程范式(Programming Paradigm)是某種編程語(yǔ)言典型的編程風(fēng)格或者說是編程方式。一種范式可以在不同的語(yǔ)言中實(shí)現(xiàn)遂跟,一種語(yǔ)言也可以同時(shí)支持多種范式逃沿。例如 JavaScript 就是一種多范式的語(yǔ)言。
(以下非原創(chuàng)借鑒寫的很好的人噠)
- 幾種常用的編程范式:
過程化(命令式)編程
:過程化編程幻锁,也被稱為命令式編程凯亮,應(yīng)該是最原始的、也是我們最熟悉的一種傳統(tǒng)的編程方式哄尔。從本質(zhì)上講假消,它是“馮.諾伊曼機(jī)“運(yùn)行機(jī)制的抽象,它的編程思維方式源于計(jì)算機(jī)指令的順序排列岭接。我們常常寫的也就是這種命令式編程富拗。事件驅(qū)動(dòng)編程
:基于事件驅(qū)動(dòng)的程序設(shè)計(jì)在圖形用戶界面(GUI)出現(xiàn)很久前就已經(jīng)被應(yīng)用于程序設(shè)計(jì)中臼予,可是只有當(dāng)圖形用戶界面廣泛流行時(shí),它才逐漸形演變?yōu)橐环N廣泛使用的程序設(shè)計(jì)模式啃沪。面向?qū)ο缶幊?/code>:過程化范式要求程序員用按部就班的算法看待每個(gè)問題粘拾。很顯然,并不是每個(gè)問題都適合這種過程化的思維方式谅阿。這也就導(dǎo)致了其它程序設(shè)計(jì)范式出現(xiàn)半哟,包括我們現(xiàn)在介紹的面向?qū)ο蟮某绦蛟O(shè)計(jì)范式。面向?qū)ο蟮某绦蛟O(shè)計(jì)包括了三個(gè)基本概念:封裝性签餐、繼承性寓涨、多態(tài)性。面向?qū)ο蟮某绦蛘Z(yǔ)言通過類氯檐、方法戒良、對(duì)象和消息傳遞,來(lái)支持面向?qū)ο蟮某绦蛟O(shè)計(jì)范式冠摄。
函數(shù)式編程
:比起過程化編程糯崎,函數(shù)式編程更加強(qiáng)調(diào)程序執(zhí)行的結(jié)果而非執(zhí)行的過程,倡導(dǎo)利用若干簡(jiǎn)單的執(zhí)行單元讓計(jì)算結(jié)果不斷漸進(jìn)河泳,逐層推導(dǎo)復(fù)雜的運(yùn)算沃呢,而不是設(shè)計(jì)一個(gè)復(fù)雜的執(zhí)行過程。
舉個(gè)例子:
// 命令式
function mysteryFn (nums) {
let squares = []
let sum = 0 // 1. 創(chuàng)建中間變量
for (let i = 0; i < nums.length; i++) {
squares.push(nums[i] * nums[i]) // 2. 循環(huán)計(jì)算平方
}
for (let i = 0; i < squares.length; i++) {
sum += squares[i] // 3. 循環(huán)累加
}
return sum
}
// 以上代碼都是 how 而不是 what...
// 函數(shù)式
const mysteryFn = (nums) => nums
.map(x => x * x) // a. 平方
.reduce((acc, cur) => acc + cur, 0) // b. 累加
響應(yīng)式
什么是響應(yīng)式拆挥?這個(gè)問題其實(shí)之前有篇里面我也講過薄霜,就是如果正常我們寫一行代碼a = b + c
,此時(shí)如果b和c都是1纸兔,那么這個(gè)時(shí)候a得到的就是2惰瓜,然后如果在這行之后b變?yōu)榱?,你需要重新執(zhí)行a = b + c
才能讓a更新為3汉矿,但是響應(yīng)式就是當(dāng)b變?yōu)?的時(shí)候c自動(dòng)就可以變成3崎坊。
RAC使用
強(qiáng)推:http://www.reibang.com/p/35a28cf0a22f
終于到了真正的使用啦,每個(gè)類都大概看一下怎么用吧~ RAC有現(xiàn)成的KVO以及UI之類的方法洲拇,可以直接用例如:
[[button1 rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
NSLog(@"button1 clicked");
}];
1. RACSignal
高階用法歡迎參考:http://www.reibang.com/p/7620edadcf88
- (void)testSignal {
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"block調(diào)用時(shí)刻:每當(dāng)有訂閱者訂閱信號(hào)奈揍,就會(huì)調(diào)用block。");
// 2.發(fā)送信號(hào)
// [subscriber sendNext:@1];
// 如果不在發(fā)送數(shù)據(jù)赋续,最好發(fā)送信號(hào)完成打月,內(nèi)部會(huì)自動(dòng)調(diào)用[RACDisposable disposable]取消訂閱信號(hào)。
[subscriber sendError:nil];
return [RACDisposable disposableWithBlock:^{
NSLog(@"信號(hào)被銷毀");
}];
}];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"signal touched 1");
}];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"signal touched 2");
}];
}
上面醬紫如果只是設(shè)置了訂閱block蚕捉,外加signal在函數(shù)結(jié)束的時(shí)候沒有引用了就會(huì)自動(dòng)銷毀,于是輸出為:
2020-08-21 23:31:04.311701+0800 Example1[98032:2252660] block調(diào)用時(shí)刻:每當(dāng)有訂閱者訂閱信號(hào)柴淘,就會(huì)調(diào)用block迫淹。
2020-08-21 23:31:04.311879+0800 Example1[98032:2252660] 信號(hào)被銷毀
2020-08-21 23:31:04.312061+0800 Example1[98032:2252660] block調(diào)用時(shí)刻:每當(dāng)有訂閱者訂閱信號(hào)秘通,就會(huì)調(diào)用block。
2020-08-21 23:31:04.312176+0800 Example1[98032:2252660] 信號(hào)被銷毀
這里的信號(hào)被銷毀
每次subscribe的時(shí)候都會(huì)調(diào)用敛熬,其實(shí)也就是signal可能對(duì)應(yīng)多個(gè)訂閱者肺稀,默認(rèn)每個(gè)訂閱者加入的時(shí)候給signal一個(gè)機(jī)會(huì)讓它在block里面給訂閱者發(fā)消息,如果它發(fā)了应民,訂閱者的next block就會(huì)執(zhí)行话原,但無(wú)論發(fā)了沒有,默認(rèn)都是給signal處理機(jī)會(huì)以后就會(huì)執(zhí)行RACDisposable dispose
解除已經(jīng)有過接手信號(hào)機(jī)會(huì)的訂閱者诲锹。
現(xiàn)在打開[subscriber sendNext:@1];
的注釋繁仁,那么由于調(diào)用了sendNext
,訂閱者的訂閱block就會(huì)被調(diào)用归园,所以signal會(huì)被touch~
2020-08-21 23:34:32.705363+0800 Example1[98144:2256354] block調(diào)用時(shí)刻:每當(dāng)有訂閱者訂閱信號(hào)黄虱,就會(huì)調(diào)用block。
2020-08-21 23:34:32.705608+0800 Example1[98144:2256354] signal touched 1
2020-08-21 23:34:32.705818+0800 Example1[98144:2256354] 信號(hào)被銷毀
2020-08-21 23:34:32.706005+0800 Example1[98144:2256354] block調(diào)用時(shí)刻:每當(dāng)有訂閱者訂閱信號(hào)庸诱,就會(huì)調(diào)用block捻浦。
2020-08-21 23:34:32.706165+0800 Example1[98144:2256354] signal touched 2
2020-08-21 23:34:32.706355+0800 Example1[98144:2256354] 信號(hào)被銷毀
原理可以參考上面的系列文章里面的第一個(gè),主要是醬紫的:
每個(gè)subscriber都遵循RACSubscriber
協(xié)議桥爽,其實(shí)有4個(gè)方法:
- (void)sendNext:(nullable id)value;
- (void)sendError:(nullable NSError *)error;
- (void)sendCompleted;
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;
error和complete其實(shí)對(duì)應(yīng)著我們?cè)谟嗛喌臅r(shí)候傳入的block朱灿,如果我們send error了,那么error的block就會(huì)執(zhí)行钠四,complete也是:
RACDisposable *dispose = [signal subscribeNext:^(id _Nullable x) {
NSLog(@"signal touched 1");
} error:^(NSError * _Nullable error) {
NSLog(@"signal error");
} completed:^{
NSLog(@"signal completed");
}];
2. RACDisposable
這個(gè)就是聽起來(lái)就知道是做什么系列了盗扒,其實(shí)是訂閱者銷毀的時(shí)候回觸發(fā)的block,RACDisposable就是對(duì)block的一層包裝:
- (instancetype)initWithBlock:(void (^)(void))block {
NSCParameterAssert(block != nil);
self = [super init];
_disposeBlock = (void *)CFBridgingRetain([block copy]);
OSMemoryBarrier();
return self;
}
訂閱者取消訂閱有兩種可能性都會(huì)觸發(fā):
- 訂閱者destroy沒有強(qiáng)引用了
- 手動(dòng)調(diào)用了
[disposable dispose]
第一種其實(shí)就是為什么我們的signal每次被訂閱都會(huì)銷毀一次形导,我們?nèi)绻靡粋€(gè)數(shù)組持有一下訂閱者环疼,那么就不會(huì)觸發(fā)銷毀啦:
- (void)testSignal {
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"block調(diào)用時(shí)刻:每當(dāng)有訂閱者訂閱信號(hào),就會(huì)調(diào)用block朵耕。");
// 2.發(fā)送信號(hào)
[subscriber sendNext:@1];
[self->subscribers addObject:subscriber];
return [RACDisposable disposableWithBlock:^{
NSLog(@"信號(hào)被銷毀");
}];
}];
RACDisposable *dispose = [signal subscribeNext:^(id _Nullable x) {
NSLog(@"signal touched 1");
} error:^(NSError * _Nullable error) {
NSLog(@"signal error");
} completed:^{
NSLog(@"signal completed");
}];
// [dispose dispose];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"signal touched 2");
}];
}
輸出:
2020-08-22 12:46:15.780694+0800 Example1[3545:41194] block調(diào)用時(shí)刻:每當(dāng)有訂閱者訂閱信號(hào)炫隶,就會(huì)調(diào)用block。
2020-08-22 12:46:15.780933+0800 Example1[3545:41194] signal touched 1
2020-08-22 12:46:15.781115+0800 Example1[3545:41194] block調(diào)用時(shí)刻:每當(dāng)有訂閱者訂閱信號(hào)阎曹,就會(huì)調(diào)用block伪阶。
2020-08-22 12:46:15.781244+0800 Example1[3545:41194] signal touched 2
這個(gè)時(shí)候就發(fā)現(xiàn)dispose沒有再調(diào)用了~ 那么如果我想讓他不通過釋放訂閱者解除訂閱要怎么做呢?打開上面注釋的[dispose dispose];
即可手動(dòng)釋放訂閱者处嫌,輸出將變?yōu)椋?/p>
2020-08-22 12:48:55.519454+0800 Example1[3626:43490] block調(diào)用時(shí)刻:每當(dāng)有訂閱者訂閱信號(hào)栅贴,就會(huì)調(diào)用block。
2020-08-22 12:48:55.519770+0800 Example1[3626:43490] signal touched 1
2020-08-22 12:48:55.519971+0800 Example1[3626:43490] 信號(hào)被銷毀
2020-08-22 12:48:55.520135+0800 Example1[3626:43490] block調(diào)用時(shí)刻:每當(dāng)有訂閱者訂閱信號(hào)熏迹,就會(huì)調(diào)用block檐薯。
2020-08-22 12:48:55.520283+0800 Example1[3626:43490] signal touched 2
注意哦,如果我們sendNext
以后調(diào)用了sendComplete
,那么其實(shí)sendComplete
里面會(huì)給你調(diào)用dispose方法解除訂閱噠坛缕。
3. RACSubject
這個(gè)RACSubject其實(shí)是繼承自RACSignal的墓猎,它更像我們以為的信號(hào),就是當(dāng) send next 觸發(fā)的時(shí)候赚楚,所有訂閱者的next block都會(huì)被觸發(fā):
- (void)testSubject {
RACSubject *subject = [RACSubject subject];
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"next1 :%@", x);
}];
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"next2 :%@", x);
}];
NSLog(@"testSubject");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subject sendNext:@"x"];
});
}
輸出:
2020-08-22 12:52:43.338328+0800 Example1[3713:45879] testSubject
2020-08-22 12:52:46.338972+0800 Example1[3713:45879] next1 :x
2020-08-22 12:52:46.339484+0800 Example1[3713:45879] next2 :x
原理其實(shí)就是它循環(huán)了一下所有的訂閱者:
- (void)sendNext:(id)value {
[self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:value];
}];
}
那么如果我想dispose一下腫么破呢毙沾?那就要看subscriber協(xié)議里面最后一個(gè)方法了:
- (void)testSubject {
RACSubject *subject = [RACSubject subject];
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"next1 :%@", x);
}];
[subject didSubscribeWithDisposable:[RACCompoundDisposable disposableWithBlock:^{
NSLog(@"disposable block1");
}]];
[subject didSubscribeWithDisposable:[RACCompoundDisposable disposableWithBlock:^{
NSLog(@"disposable block2");
}]];
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"next2 :%@", x);
}];
NSLog(@"testSubject");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subject sendNext:@"x"];
});
}
輸出:
2020-08-22 12:53:56.902050+0800 Example1[4259:50201] testSubject
2020-08-22 12:53:59.902840+0800 Example1[4259:50201] next1 :x
2020-08-22 12:53:59.903351+0800 Example1[4259:50201] next2 :x
2020-08-22 12:53:59.903802+0800 Example1[4259:50201] disposable block1
2020-08-22 12:53:59.904165+0800 Example1[4259:50201] disposable block2
也就是在signal銷毀的時(shí)候會(huì)觸發(fā)我們加入的disposable們,并且是按照加入的順序觸發(fā)噠宠页。
4. RACReplaySubject
這個(gè)其實(shí)和subject很類似左胞,區(qū)別就是這個(gè)是可以先發(fā)信號(hào)再訂閱,也就是新的訂閱者訂閱的時(shí)候举户,可以拿到之前發(fā)過的所有歷史信息:
- (void)testReplaySubject {
RACReplaySubject *subject = [RACReplaySubject subject];
[subject sendNext:@"hhh1"];
[subject sendNext:@"hhh2"];
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"value received: %@", x);
}];
NSLog(@"finished");
}
輸出:
2020-08-22 12:57:29.315784+0800 Example1[4367:53504] value received: hhh1
2020-08-22 12:57:29.316010+0800 Example1[4367:53504] value received: hhh2
2020-08-22 12:57:29.316146+0800 Example1[4367:53504] finished
這個(gè)原理其實(shí)就是在sendNext的時(shí)候把數(shù)據(jù)存到了一個(gè)數(shù)組里面烤宙,然后訂閱者訂閱的時(shí)候會(huì)先把這個(gè)數(shù)組遍歷一次:
- (void)sendNext:(id)value {
@synchronized (self) {
[self.valuesReceived addObject:value ?: RACTupleNil.tupleNil];
if (self.capacity != RACReplaySubjectUnlimitedCapacity && self.valuesReceived.count > self.capacity) {
[self.valuesReceived removeObjectsInRange:NSMakeRange(0, self.valuesReceived.count - self.capacity)];
}
[super sendNext:value];
}
}
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
@synchronized (self) {
for (id value in self.valuesReceived) {
if (compoundDisposable.disposed) return;
[subscriber sendNext:(value == RACTupleNil.tupleNil ? nil : value)];
}
……
}
}];
[compoundDisposable addDisposable:schedulingDisposable];
return compoundDisposable;
}
5. RACScheduler
Scheduler其實(shí)是對(duì)GCD的一個(gè)封裝,主要是做調(diào)度的:
- (void)testScheduler {
NSLog(@"started");
[[RACScheduler scheduler] schedule:^{
NSLog(@"當(dāng)前線程:%@",[NSThread currentThread]);
}];
NSLog(@"finished");
}
輸出:
2020-08-22 13:02:05.910901+0800 Example1[4478:57139] started
2020-08-22 13:02:05.911190+0800 Example1[4478:57139] finished
2020-08-22 13:02:05.911413+0800 Example1[4478:57289] 當(dāng)前線程:<NSThread: 0x600001200d80>{number = 3, name = (null)}
RACScheduler是一個(gè)父類敛摘,他有很多子類:
我們用到的直接其實(shí)是:
+ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name {
return [[RACTargetQueueScheduler alloc] initWithName:name targetQueue:dispatch_get_global_queue(priority, 0)];
}
@interface RACTargetQueueScheduler : RACQueueScheduler
看RACQueueScheduler的名字也可以知道這個(gè)scheduler其實(shí)是一個(gè)依賴于queue執(zhí)行的:
- (RACDisposable *)schedule:(void (^)(void))block {
NSCParameterAssert(block != NULL);
RACDisposable *disposable = [[RACDisposable alloc] init];
dispatch_async(self.queue, ^{
if (disposable.disposed) return;
[self performAsCurrentScheduler:block];
});
return disposable;
}
也就是說门烂,它的執(zhí)行順序依賴于我們傳入的是并行還是串行queue。
我們通過[RACScheduler scheduler]
創(chuàng)建的其實(shí)傳入的是global queue兄淫,也就是一個(gè)并行的隊(duì)列屯远,下面來(lái)嘗試一下:
- (void)testScheduler {
NSLog(@"started");
[[RACScheduler scheduler] schedule:^{
NSLog(@"當(dāng)前線程1:%@",[NSThread currentThread]);
sleep(3);
NSLog(@"當(dāng)前線程1:%@",[NSThread currentThread]);
}];
[[RACScheduler scheduler] schedule:^{
NSLog(@"當(dāng)前線程2:%@",[NSThread currentThread]);
sleep(3);
NSLog(@"當(dāng)前線程2:%@",[NSThread currentThread]);
}];
NSLog(@"finished");
}
輸出:
2020-08-22 13:09:57.205527+0800 Example1[4663:63879] started
2020-08-22 13:09:57.205864+0800 Example1[4663:63879] finished
2020-08-22 13:09:57.206289+0800 Example1[4663:63989] 當(dāng)前線程1:<NSThread: 0x6000035a4640>{number = 4, name = (null)}
2020-08-22 13:09:57.206331+0800 Example1[4663:63988] 當(dāng)前線程2:<NSThread: 0x6000035f07c0>{number = 5, name = (null)}
2020-08-22 13:10:00.207777+0800 Example1[4663:63989] 當(dāng)前線程1:<NSThread: 0x6000035a4640>{number = 4, name = (null)}
2020-08-22 13:10:00.207825+0800 Example1[4663:63988] 當(dāng)前線程2:<NSThread: 0x6000035f07c0>{number = 5, name = (null)}
RACScheduler還有其他的獲取方式:
- immediateScheduler,立即執(zhí)行的線程捕虽,其實(shí)就是在當(dāng)前線程執(zhí)行的
- mainThreadScheduler慨丐,獲取主線程調(diào)度器
- currentScheduler,就是獲取當(dāng)前線程調(diào)度器泄私,但不會(huì)立即執(zhí)行
- (void)testScheduler {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"當(dāng)前線程:%@",[NSThread currentThread]);
NSLog(@"started");
[[RACScheduler immediateScheduler] schedule:^{
NSLog(@"當(dāng)前線程2:%@",[NSThread currentThread]);
sleep(3);
NSLog(@"當(dāng)前線程2:%@",[NSThread currentThread]);
}];
NSLog(@"finished");
});
}
輸出:
2020-08-22 21:26:46.562790+0800 Example1[7651:88680] started
2020-08-22 21:26:46.563918+0800 Example1[7651:88742] 當(dāng)前線程:<NSThread: 0x6000038905c0>{number = 7, name = (null)}
2020-08-22 21:26:46.564143+0800 Example1[7651:88742] started
2020-08-22 21:26:46.564534+0800 Example1[7651:88742] 當(dāng)前線程2:<NSThread: 0x6000038905c0>{number = 7, name = (null)}
2020-08-22 21:26:49.569298+0800 Example1[7651:88742] 當(dāng)前線程2:<NSThread: 0x6000038905c0>{number = 7, name = (null)}
2020-08-22 21:26:49.569719+0800 Example1[7651:88742] finished
如果換成mainThreadScheduler
輸出就會(huì)是醬紫房揭,不再是同步調(diào)用啦:
2020-08-22 21:29:35.556247+0800 Example1[7754:91485] 當(dāng)前線程:<NSThread: 0x600000b249c0>{number = 4, name = (null)}
2020-08-22 21:29:35.556581+0800 Example1[7754:91485] started
2020-08-22 21:29:35.556826+0800 Example1[7754:91485] finished
2020-08-22 21:29:35.579004+0800 Example1[7754:91405] 當(dāng)前線程2:<NSThread: 0x600000b61080>{number = 1, name = main}
2020-08-22 21:29:38.579679+0800 Example1[7754:91405] 當(dāng)前線程2:<NSThread: 0x600000b61080>{number = 1, name = main}
如果換成currentScheduler
是醬紫的,按理說也是異步的晌端,但實(shí)際輸出是醬紫的:
2020-08-22 21:31:23.592051+0800 Example1[7841:93828] 當(dāng)前線程:<NSThread: 0x60000340dfc0>{number = 5, name = (null)}
2020-08-22 21:31:23.592302+0800 Example1[7841:93828] started
2020-08-22 21:31:23.592497+0800 Example1[7841:93828] finished
為啥沒執(zhí)行schedule的內(nèi)容呢捅暴?
+ (RACScheduler *)currentScheduler {
RACScheduler *scheduler = NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey];
if (scheduler != nil) return scheduler;
if ([self.class isOnMainThread]) return RACScheduler.mainThreadScheduler;
return nil;
}
如果當(dāng)前線程之前沒有創(chuàng)建scheduler并且又不是主線程,那么就會(huì)return一個(gè)nil咧纠,自然就不會(huì)執(zhí)行啦蓬痒。
- scheduler還可以通過優(yōu)先級(jí)來(lái)創(chuàng)建,例如醬紫:
- (void)testScheduler {
[[RACScheduler schedulerWithPriority:RACSchedulerPriorityLow] schedule:^{
NSLog(@"scheduler:%@",[RACScheduler currentScheduler]);
NSLog(@"當(dāng)前線程:%@",[NSThread currentThread]);
}];
}
輸出:
2020-08-22 21:35:24.544379+0800 Example1[8014:98226] scheduler:<RACTargetQueueScheduler: 0x600001476a20> org.reactivecocoa.ReactiveObjC.RACScheduler.backgroundScheduler
2020-08-22 21:35:24.544993+0800 Example1[8014:98226] 當(dāng)前線程:<NSThread: 0x600000167900>{number = 5, name = (null)}
RAC優(yōu)先級(jí)其實(shí)是對(duì)應(yīng)GCD的優(yōu)先級(jí)的:
typedef enum : long {
RACSchedulerPriorityHigh = DISPATCH_QUEUE_PRIORITY_HIGH,
RACSchedulerPriorityDefault = DISPATCH_QUEUE_PRIORITY_DEFAULT,
RACSchedulerPriorityLow = DISPATCH_QUEUE_PRIORITY_LOW,
RACSchedulerPriorityBackground = DISPATCH_QUEUE_PRIORITY_BACKGROUND,
} RACSchedulerPriority;
不同優(yōu)先級(jí)其實(shí)就是對(duì)應(yīng)了gcd不同的queue漆羔,注意這里默認(rèn)是global的queue哈:
+ (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name {
return [[RACTargetQueueScheduler alloc] initWithName:name targetQueue:dispatch_get_global_queue(priority, 0)];
}
Finally梧奢,為什么RAC要封裝GCD呢?這個(gè)其實(shí)我感覺是為了更好用和好看演痒,dispatch很多縮進(jìn)亲轨,但是RAC如果可以直接delivery就會(huì)好看很多~ 希望有大佬來(lái)教學(xué)一下這個(gè)問題~
6. RACMulticastConnection
如果我們通過signal做網(wǎng)絡(luò)請(qǐng)求可以醬紫:
- (void)testConn {
RACSignal * signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"發(fā)送網(wǎng)絡(luò)請(qǐng)求");
sleep(3);
[subscriber sendNext:@"得到網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)"];
return nil;
}];
[signal subscribeNext:^(id x) {
NSLog(@"1 - %@",x);
}];
[signal subscribeNext:^(id x) {
NSLog(@"2 - %@",x);
}];
[signal subscribeNext:^(id x) {
NSLog(@"3 - %@",x);
}];
}
輸出:
2020-08-22 21:46:45.095715+0800 Example1[8973:108249] 發(fā)送網(wǎng)絡(luò)請(qǐng)求
2020-08-22 21:46:48.097432+0800 Example1[8973:108249] 1 - 得到網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)
2020-08-22 21:46:48.098200+0800 Example1[8973:108249] 發(fā)送網(wǎng)絡(luò)請(qǐng)求
2020-08-22 21:46:51.099029+0800 Example1[8973:108249] 2 - 得到網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)
2020-08-22 21:46:51.099650+0800 Example1[8973:108249] 發(fā)送網(wǎng)絡(luò)請(qǐng)求
2020-08-22 21:46:54.101352+0800 Example1[8973:108249] 3 - 得到網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)
就可能會(huì)重復(fù)觸發(fā),RACMulticastConnection
就是用于避免這個(gè)事兒的:
- (void)testConn {
RACSignal * signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"發(fā)送網(wǎng)絡(luò)請(qǐng)求");
sleep(3);
[subscriber sendNext:@"得到網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)"];
return nil;
}];
RACMulticastConnection *connect = [signal publish];
[connect.signal subscribeNext:^(id x) {
NSLog(@"1 - %@",x);
}];
[connect.signal subscribeNext:^(id x) {
NSLog(@"2 - %@",x);
}];
[connect.signal subscribeNext:^(id x) {
NSLog(@"3 - %@",x);
}];
[connect connect];
}
輸出:
2020-08-22 21:57:56.231520+0800 Example1[9697:117688] 發(fā)送網(wǎng)絡(luò)請(qǐng)求
2020-08-22 21:57:59.232046+0800 Example1[9697:117688] 1 - 得到網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)
2020-08-22 21:57:59.232525+0800 Example1[9697:117688] 2 - 得到網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)
2020-08-22 21:57:59.232854+0800 Example1[9697:117688] 3 - 得到網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)
它的大概原理是醬紫的鸟顺,感興趣的可以看之前的參考:
其實(shí)就是conn的signal其實(shí)是一個(gè)subject惦蚊,而非之前的RACSignal,所以可以訂閱好幾個(gè)。注意哦蹦锋,signal訂閱的時(shí)候是順序執(zhí)行的曾撤,需要先執(zhí)行創(chuàng)建時(shí)候每次訂閱觸發(fā)的block,之后才能走下一個(gè)訂閱晕粪;而subject比較正常,是可以先訂閱的渐裸,然后一起等待sendNext的觸發(fā)
7. RACCommand
這個(gè)東西其實(shí)我也一直很迷茫是干啥用的~ 參考了:http://www.reibang.com/p/dc472c644e7b
一般情況下巫湘,RACCommand主要用來(lái)封裝一些請(qǐng)求、事件等昏鹃,舉個(gè)例子尚氛,我們的tableView在下拉滾動(dòng)時(shí)若想刷新數(shù)據(jù)需要向接口提供頁(yè)碼或者最后一個(gè)數(shù)據(jù)的ID,我們可以把請(qǐng)求封裝進(jìn)RACCommand里洞渤,想要獲取數(shù)據(jù)的時(shí)候只要將頁(yè)碼或者ID傳入RACCommand里就可以了阅嘶,同時(shí)監(jiān)控RACCommand何時(shí)完成,若完成后將數(shù)據(jù)加入到tableview的數(shù)組就可以了,這是一個(gè)平常用的比較多的場(chǎng)景载迄。使用是主要有三個(gè)注意點(diǎn):
- RACCommand必須返回信號(hào),信號(hào)可以為空
- RACCommand必須強(qiáng)引用
- RACCommand發(fā)送完數(shù)據(jù)必須發(fā)送完成信號(hào)
例如醬紫:
-(void)loadInfo{
//input就是控制器中,viewmodel執(zhí)行command時(shí)excute傳入的參數(shù)
RACCommand * command = [[RACCommand alloc]initWithSignalBlock:^RACSignal *(id input) {
//command必須有信號(hào)返回值,如果沒有的話可以為[RACSignal empty]
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber)
{
NSMutableDictionary * params = [NSMutableDictionary dictionary];
params[@"build"] = @"3360";
params[@"channel"] = @"appstore";
params[@"plat"] = @"2";
[FYRequestTool GET:@"http://app.bilibili.com/x/banner" parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
[subscriber sendNext:responseObject];
//發(fā)送完信號(hào)必須發(fā)送完成信號(hào),否則無(wú)法執(zhí)行
[subscriber sendCompleted];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[subscriber sendError:error];
}];
return [RACDisposable disposableWithBlock:^{
[FYRequestTool cancel];
NSLog(@"這里面可以寫取消請(qǐng)求,完成信號(hào)后請(qǐng)求會(huì)取消");
}];
}];
}];
//必須強(qiáng)引用這個(gè)command,否則無(wú)法執(zhí)行
self.command = command;
}
signal如果我們?cè)谟嗛喌腷lock里面去做網(wǎng)絡(luò)請(qǐng)求讯柔,是無(wú)法帶入一些參數(shù)給它的,但是command可以通過execute的參數(shù)做區(qū)分护昧,然后你可以返回一個(gè)signal魂迄,在signal內(nèi)部通過不同input做不同的事情,最后sendNext一下觸發(fā)訂閱者的block告知訂閱者并返回拿到的數(shù)據(jù):
- (void)testCommand {
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) {
NSLog(@"create command 1 with input:%@", input);
sleep(3);
NSLog(@"create command 2");
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
// 這里可以拿input去做不同的事情
NSLog(@"signal subscribed 1 with input:%@", input);
sleep(3);
[subscriber sendNext:@"a"];
NSLog(@"signal subscribed 2");
return [RACDisposable disposableWithBlock:^{
NSLog(@"RACDisposable disposable block");
}];
}];
}];
NSLog(@"testCommand");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSString *input = @"執(zhí)行";
[[command.executionSignals switchToLatest] subscribeNext:^(id _Nullable x) {
NSLog(@"switchToLatest-->%@",x);
}];
[command execute:input];
});
}
輸出:
2020-08-22 22:25:33.442545+0800 Example1[10798:139387] testCommand
2020-08-22 22:25:36.443640+0800 Example1[10798:139387] create command 1 with input:執(zhí)行
2020-08-22 22:25:39.445241+0800 Example1[10798:139387] create command 2
2020-08-22 22:25:39.447601+0800 Example1[10798:139387] executing-->0
2020-08-22 22:25:39.448927+0800 Example1[10798:139387] executing-->1
2020-08-22 22:25:39.449554+0800 Example1[10798:139387] signal subscribed 1 with input:執(zhí)行
2020-08-22 22:25:42.450867+0800 Example1[10798:139387] switchToLatest-->a
2020-08-22 22:25:42.451391+0800 Example1[10798:139387] signal subscribed 2
2020-08-22 22:25:42.451810+0800 Example1[10798:139387] RACDisposable disposable block
8. bind
bind其實(shí)是RACStream的方法惋耙,但是很多類都復(fù)寫了它捣炬,例如signal的:
- (RACSignal *)bind:(RACSignalBindBlock (^)(void))block {
NSCParameterAssert(block != NULL);
/*
* -bind: should:
*
* 1. Subscribe to the original signal of values.
* 2. Any time the original signal sends a value, transform it using the binding block.
* 3. If the binding block returns a signal, subscribe to it, and pass all of its values through to the subscriber as they're received.
* 4. If the binding block asks the bind to terminate, complete the _original_ signal.
* 5. When _all_ signals complete, send completed to the subscriber.
*
* If any signal sends an error at any point, send that to the subscriber.
*/
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
RACSignalBindBlock bindingBlock = block();
__block volatile int32_t signalCount = 1; // indicates self
RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
void (^completeSignal)(RACDisposable *) = ^(RACDisposable *finishedDisposable) {
if (OSAtomicDecrement32Barrier(&signalCount) == 0) {
[subscriber sendCompleted];
[compoundDisposable dispose];
} else {
[compoundDisposable removeDisposable:finishedDisposable];
}
};
void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
OSAtomicIncrement32Barrier(&signalCount);
RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
[compoundDisposable addDisposable:selfDisposable];
RACDisposable *disposable = [signal subscribeNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
[compoundDisposable dispose];
[subscriber sendError:error];
} completed:^{
@autoreleasepool {
completeSignal(selfDisposable);
}
}];
selfDisposable.disposable = disposable;
};
@autoreleasepool {
RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
[compoundDisposable addDisposable:selfDisposable];
RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
// Manually check disposal to handle synchronous errors.
if (compoundDisposable.disposed) return;
BOOL stop = NO;
id signal = bindingBlock(x, &stop);
@autoreleasepool {
if (signal != nil) addSignal(signal);
if (signal == nil || stop) {
[selfDisposable dispose];
completeSignal(selfDisposable);
}
}
} error:^(NSError *error) {
[compoundDisposable dispose];
[subscriber sendError:error];
} completed:^{
@autoreleasepool {
completeSignal(selfDisposable);
}
}];
selfDisposable.disposable = bindingDisposable;
}
return compoundDisposable;
}] setNameWithFormat:@"[%@] -bind:", self.name];
}
其實(shí)就是創(chuàng)建了一個(gè)新的signal黔龟,然后在這個(gè)信號(hào)被訂閱的時(shí)候會(huì)執(zhí)行我們bind時(shí)候提供的block芍阎,根據(jù)我們?cè)赽ind里面返回的signal是不是空來(lái)決定是要addSignal還是addComplete至非。
注意哦之所以后面signal被觸發(fā)的時(shí)候习劫,可以給subscriber send next主要是因?yàn)樵?code>addSignal里面有引用subscriber哦到踏。
所以其實(shí)bind主要做的是:
正常來(lái)說我們發(fā)送信號(hào)只有信號(hào)的訂閱者才會(huì)接收到消息拦宣,而bind會(huì)將信號(hào)攔截過濾后發(fā)送到新的信號(hào)訂閱者中擒权。
它的流程是醬紫的:
- 原信號(hào)->bind(生成新信號(hào))
- 原信號(hào)發(fā)送消息->bind Block 進(jìn)行過濾乏沸,然后發(fā)送消息到內(nèi)部一個(gè)信號(hào)中-> 轉(zhuǎn)發(fā)到bind時(shí)生成的新信息號(hào)中
舉個(gè)例子:
- (void)testBind {
RACSubject *subject = [RACSubject subject];
RACSignal * signal = [subject bind:^RACSignalBindBlock _Nonnull{
NSLog(@"bind block");
return ^RACSignal *(id _Nullable value, BOOL *stop){
NSLog(@"signal block");
return [RACReturnSignal return:value];
};
}];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"收到的數(shù)據(jù) - %@",x);
}];
[subject sendNext:@"啟動(dòng)自毀程序"];
}
輸出:
2020-08-23 08:45:52.755609+0800 Example1[2023:32254] bind block
2020-08-23 08:45:52.756066+0800 Example1[2023:32254] signal block
2020-08-23 08:45:52.756573+0800 Example1[2023:32254] 收到的數(shù)據(jù) - 啟動(dòng)自毀程序
在rac里面也有很多地方用到了bind美莫,比如flattenMap
方法,其實(shí)就是bind的封裝厢呵。內(nèi)部就是bind方法的調(diào)用,然后可以傳入一個(gè)block 作為過濾的規(guī)則赐劣。
9. RACStream
這個(gè)part可以參考:http://www.reibang.com/p/0e4de0eeaff7 & http://www.reibang.com/p/64ab974445dd
RACStream中的許多定義都是抽象的哩都,沒有具體實(shí)現(xiàn)漠嵌,需要由其子類進(jìn)行實(shí)現(xiàn)咐汞。
例如signal其實(shí)是繼承自stream的:
@interface RACSignal<__covariant ValueType> : RACStream
- empty
+ (__kindof RACStream *)empty;
因?yàn)镽AC中nil會(huì)導(dǎo)致crash,所以很多時(shí)候需要定義一個(gè)空對(duì)象來(lái)替代nil儒鹿,一般empty都被創(chuàng)建為一個(gè)單例來(lái)使用化撕。
- bind
適用于過濾
- (__kindof RACStream *)bind:(RACStreamBindBlock (^)(void))block;
懶綁定,將block返回的RACStream綁定到自身约炎,在調(diào)用時(shí)才執(zhí)行內(nèi)部操作植阴。很多操作都是基于bind的,比如flattenMap圾浅,大多時(shí)候我們都調(diào)用bind的上層方法掠手。
例如,創(chuàng)建一個(gè)signal對(duì)象發(fā)送十次值,并且在其中隨機(jī)發(fā)送@" "贱傀。我們要做的是過濾掉@" "并且在接收到@"5"時(shí)終止后續(xù)發(fā)送返回一個(gè)新的signal并且發(fā)送@"a"惨撇,@"b":
RACSignal * signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
//發(fā)送10次值
for (int i = 0; i < 10; i ++) {
//隨機(jī)發(fā)送 @" "
BOOL empty = arc4random_uniform(2);
if (!empty || i == 5) {
[subscriber sendNext:[NSString stringWithFormat:@"%zd",i]];
}
else {
[subscriber sendNext:@" "];
}
}
[subscriber sendCompleted];
return nil;
}] bind:^RACStreamBindBlock{
return ^id (NSString * value, BOOL * stop) {
if ([value isEqualToString:@" "]) { //過濾@" "
return [RACSignal empty];
}
else if ([value isEqualToString:@"5"]){ //接收到 @"5" 終止
* stop = YES;
RACSignal * result = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"a"];
[subscriber sendNext:@"b"];
[subscriber sendCompleted];
return nil;
}];
return result;
}
else {
return [RACSignal return:value];
}
};
}];
[signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[signal subscribeCompleted:^{
NSLog(@"complete");
}];
/* 某次執(zhí)行結(jié)果
2018-03-27 15:14:02.334078+0800 ReactCocoaTest[91664:2403921] 1
2018-03-27 15:14:02.334390+0800 ReactCocoaTest[91664:2403921] 3
2018-03-27 15:14:02.334562+0800 ReactCocoaTest[91664:2403921] 4
2018-03-27 15:14:02.334836+0800 ReactCocoaTest[91664:2403921] a
2018-03-27 15:14:02.334957+0800 ReactCocoaTest[91664:2403921] b
2018-03-27 15:14:02.335673+0800 ReactCocoaTest[91664:2403921] complete
*/
- return
+ (__kindof RACStream *)return:(id)value;
把一個(gè)值包裝成對(duì)應(yīng)的RACStream的子類型。
- concat
適用于連續(xù)的異步任務(wù)
- (__kindof RACStream *)concat:(RACStream *)stream;
連接兩個(gè)信號(hào)府寒,子類實(shí)現(xiàn)具體如何連接魁衙。
例如醬紫:
RACSignal * signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"1"];
[[RACScheduler mainThreadScheduler]afterDelay:5 schedule:^{
[subscriber sendCompleted];
}];
return nil;
}];
RACSignal * signal2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"2"];
[[RACScheduler mainThreadScheduler]afterDelay:5 schedule:^{
[subscriber sendCompleted];
}];
return nil;
}];
RACSignal * concat = [signal1 concat:signal2];
[concat subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[concat subscribeCompleted:^{
NSLog(@"complete");
}];
[concat subscribeError:^(NSError *error) {
NSLog(@"error");
}];
/*
2018-03-27 16:37:18.546755+0800 ReactCocoaTest[94470:2467226] 1
2018-03-27 16:37:24.040041+0800 ReactCocoaTest[94470:2467226] 2
2018-03-27 16:37:29.513247+0800 ReactCocoaTest[94470:2467226] complete
*/
- zipWith
適用于連接兩個(gè)信號(hào)的結(jié)果
- (__kindof RACStream *)zipWith:(RACStream *)stream;
壓縮兩個(gè)信號(hào),子類實(shí)現(xiàn)具體如何壓縮株搔。
例如醬紫:
RACSignal * signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"1"];
[subscriber sendNext:@"2"];
[subscriber sendNext:@"3"];
[subscriber sendNext:@"4"];
[subscriber sendNext:@"5"];
[subscriber sendNext:@"6"];
[[RACScheduler mainThreadScheduler]afterDelay:7 schedule:^{
[subscriber sendError:nil];
}];
return nil;
}];
RACSignal * signal2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"a"];
[subscriber sendNext:@"b"];
[[RACScheduler mainThreadScheduler]afterDelay:5 schedule:^{
[subscriber sendNext:@"d"];
[subscriber sendCompleted];
}];
return nil;
}];
RACSignal * zip = [signal1 zipWith:signal2];
[zip subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[zip subscribeCompleted:^{
NSLog(@"complete");
}];
[zip subscribeError:^(NSError *error) {
NSLog(@"error");
}];
/*
2018-03-27 17:20:15.922457+0800 ReactCocoaTest[96661:2499724] <RACTuple: 0x604000206030> (
1,
a
)
2018-03-27 17:20:15.922787+0800 ReactCocoaTest[96661:2499724] <RACTuple: 0x604000206010> (
2,
b
)
2018-03-27 17:20:21.414965+0800 ReactCocoaTest[96661:2499724] <RACTuple: 0x604000207110> (
3,
d
)
2018-03-27 17:20:21.415268+0800 ReactCocoaTest[96661:2499724] complete
*/
10. RACTuple
RACTuple就和swift里面的元組一樣的剖淀,隨便放數(shù)據(jù),它的內(nèi)部主要就是一個(gè)可以放 id 類型的數(shù)組:
- (instancetype)initWithBackingArray:(NSArray *)backingArray {
self = [super init];
_backingArray = [backingArray copy];
return self;
}
使用就很簡(jiǎn)單纤房,可以用init纵隔,也可以用提供的快捷方式:
@class RACTwoTuple<__covariant First, __covariant Second>;
@class RACThreeTuple<__covariant First, __covariant Second, __covariant Third>;
@class RACFourTuple<__covariant First, __covariant Second, __covariant Third, __covariant Fourth>;
@class RACFiveTuple<__covariant First, __covariant Second, __covariant Third, __covariant Fourth, __covariant Fifth>;
11. RACSequence
它也是繼承RACStream
的,所以也有各種zip炮姨、concat捌刮、bind的操作~
RAC對(duì)OC的集合和RACTuple進(jìn)行Category擴(kuò)充,因此可用集合.rac_sequence
舒岸,把集合快速轉(zhuǎn)換成RACSequence對(duì)象
訂閱RACSequence的signal绅作,可遍歷所有元素,但因?yàn)閮?nèi)部實(shí)現(xiàn)是異步執(zhí)行的(for in是在當(dāng)前線程)蛾派,所以使用時(shí)候需要注意時(shí)間順序俄认。
NSArray *array = @[@1, @2, @3];
NSDictionary *dict = @{@"key1" : @"value1", @"key2" : @"value2", @"key3" : @"value3"};
NSString *str = @"ABC";
NSSet *set = [NSSet setWithArray:array];
RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:array];
//NSArray 會(huì)返回元素
[array.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"array rac_sequence : %@", x);
}];
//NSDictionary 會(huì)返回打包成Tuple的key个少、value
[dict.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"dict rac_sequence : %@", x);
}];
//NSString 會(huì)返回單個(gè)字符
[str.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"str rac_sequence : %@", x);
}];
//NSSet 會(huì)返回元素
[set.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"set rac_sequence : %@", x);
}];
//RACTuple 會(huì)返回內(nèi)置數(shù)組的元素
[tuple.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"tuple rac_sequence : %@", x);
}];
//以下是輸出結(jié)果,從結(jié)果可以看出眯杏,結(jié)果是亂序的
//如果再打印當(dāng)前線程夜焦,會(huì)發(fā)現(xiàn)每種集合都在各自的一條線程上輸出,但非主線程
//array rac_sequence : 1
//array rac_sequence : 2
//array rac_sequence : 3
//str rac_sequence : A
//set rac_sequence : 3
//str rac_sequence : B
//tuple rac_sequence : 1
//set rac_sequence : 2
//dict rac_sequence : <RACTuple: 0x610000004cf0> (key1,value1)
//tuple rac_sequence : 2
//str rac_sequence : C
//set rac_sequence : 1
//dict rac_sequence : <RACTuple: 0x610000004ce0> (key3,value3)
//tuple rac_sequence : 3
//dict rac_sequence : <RACTuple: 0x608000004bb0> (key2,value2)
How?
RAC的信號(hào)的用法主要有什么呢岂贩?我們?cè)贛VVM或者M(jìn)VC框架中都會(huì)有綁定V和 VM或者M(jìn) 的操作茫经,這個(gè)時(shí)候可以用signal來(lái)實(shí)現(xiàn),比如view來(lái)訂閱signal萎津,然后VM在獲取完數(shù)據(jù)以后可以sendNext把數(shù)據(jù)傳給view即可科平。
為什么 RAC 是 響應(yīng)式 & 函數(shù)式?
首先響應(yīng)體現(xiàn)在 signal 上面姜性,比如當(dāng)我們點(diǎn)擊了一個(gè)button,就會(huì)發(fā)給我們一個(gè) signal 髓考,我們可以在接受到 signal 以后做一些處理部念,這個(gè)就類似于我們?cè)O(shè)置在接受到 b 改變的 signal 里面設(shè)置a = b + c,那么每次 b 變化氨菇,a 都會(huì)自然的變化儡炼,不需要再手動(dòng)改啦。
函數(shù)式比較隱密查蓉,因?yàn)镺C里面函數(shù)是不能作為參數(shù)傳遞的乌询,函數(shù)式只能體現(xiàn)在block上面,所以RAC里面通過block響應(yīng)next就是函數(shù)式的體現(xiàn)了豌研,和swift的promise里面的.then().next()之類的很類似妹田。
References:
http://www.reibang.com/p/5fc71f541b1c
http://www.reibang.com/p/d90f6715f2cc
https://medium.com/@shaistha24/functional-programming-vs-object-oriented-programming-oop-which-is-better-82172e53a526
http://www.reibang.com/p/9eee5a0b42da