前言
緊接著上篇的源碼實(shí)現(xiàn)分析道偷,繼續(xù)分析RACSignal的變換操作的底層實(shí)現(xiàn)匾鸥。
目錄
- 1.高階信號(hào)操作
- 2.同步操作
- 3.副作用操作
- 4.多線程操作
- 5.其他操作
一. 高階信號(hào)操作
高階操作大部分的操作是針對(duì)高階信號(hào)的桅滋,也就是說信號(hào)里面發(fā)送的值還是一個(gè)信號(hào)或者是一個(gè)高階信號(hào)∩桶耄可以類比數(shù)組砌左,這里就是多維數(shù)組,數(shù)組里面還是套的數(shù)組悦施。
1. flattenMap: (在父類RACStream中定義的)
flattenMap:在整個(gè)RAC中具有很重要的地位并扇,很多信號(hào)變換都是可以用flattenMap:來實(shí)現(xiàn)的。
map:抡诞,flatten穷蛹,filter,sequenceMany:這4個(gè)操作都是用flattenMap:來實(shí)現(xiàn)的昼汗。然而其他變換操作實(shí)現(xiàn)里面用到map:肴熏,flatten,filter又有很多顷窒。
回顧一下map:的實(shí)現(xiàn):
- (instancetype)map:(id (^)(id value))block {
NSCParameterAssert(block != nil);
Class class = self.class;
return [[self flattenMap:^(id value) {
return [class return:block(value)];
}] setNameWithFormat:@"[%@] -map:", self.name];
}
map:的操作其實(shí)就是直接原信號(hào)進(jìn)行的 flattenMap:的操作蛙吏,變換出來的新的信號(hào)的值是block(value)源哩。
flatten的實(shí)現(xiàn)接下去會(huì)具體分析,這里先略過鸦做。
filter的實(shí)現(xiàn):
- (instancetype)filter:(BOOL (^)(id value))block {
NSCParameterAssert(block != nil);
Class class = self.class;
return [[self flattenMap:^ id (id value) {
block(value) ? return [class return:value] : return class.empty;
}] setNameWithFormat:@"[%@] -filter:", self.name];
}
filter的實(shí)現(xiàn)和map:有點(diǎn)類似励烦,也是對(duì)原信號(hào)進(jìn)行 flattenMap:的操作,只不過block(value)不是作為返回值泼诱,而是作為判斷條件坛掠,滿足這個(gè)閉包的條件,變換出來的新的信號(hào)返回值就是value治筒,不滿足的就返回empty信號(hào)
接下去要分析的高階操作里面屉栓,switchToLatest,try:耸袜,tryMap:的實(shí)現(xiàn)中也將會(huì)使用到flattenMap:友多。
flattenMap:的源碼實(shí)現(xiàn):
- (instancetype)flattenMap:(RACStream * (^)(id value))block {
Class class = self.class;
return [[self bind:^{
return ^(id value, BOOL *stop) {
id stream = block(value) ?: [class empty];
NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);
return stream;
};
}] setNameWithFormat:@"[%@] -flattenMap:", self.name];
}
flattenMap:的實(shí)現(xiàn)是調(diào)用了bind函數(shù),對(duì)原信號(hào)進(jìn)行變換堤框,并返回block(value)的新信號(hào)域滥。關(guān)于bind操作的具體流程這篇文章里面已經(jīng)分析過了,這里不再贅述蜈抓。
從flattenMap:的源碼可以看到骗绕,它是可以支持類似Promise的串行異步操作的,并且flattenMap:是滿足Monad中bind部分定義的资昧。flattenMap:沒法去實(shí)現(xiàn)takeUntil:和take:的操作。
然而荆忍,bind操作可以實(shí)現(xiàn)take:的操作格带,bind是完全滿足Monad中bind部分定義的。
2. flatten (在父類RACStream中定義的)
flatten的源碼實(shí)現(xiàn):
- (instancetype)flatten {
__weak RACStream *stream __attribute__((unused)) = self;
return [[self flattenMap:^(id value) {
return value;
}] setNameWithFormat:@"[%@] -flatten", self.name];
}
flatten操作必須是對(duì)高階信號(hào)進(jìn)行操作刹枉,如果信號(hào)里面不是信號(hào)叽唱,即不是高階信號(hào),那么就會(huì)崩潰微宝。崩潰信息如下:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Value returned from -flattenMap: is not a stream
所以flatten是對(duì)高階信號(hào)進(jìn)行的降階操作棺亭。高階信號(hào)每發(fā)送一次信號(hào),經(jīng)過flatten變換蟋软,由于flattenMap:操作之后镶摘,返回的新的信號(hào)的每個(gè)值就是原信號(hào)中每個(gè)信號(hào)的值。
如果對(duì)信號(hào)A岳守,信號(hào)B凄敢,信號(hào)C進(jìn)行merge:操作,可以達(dá)到和flatten一樣的效果湿痢。
[RACSignal merge:@[signalA,signalB,signalC]];
merge:操作在上篇文章分析過涝缝,再來復(fù)習(xí)一下:
+ (RACSignal *)merge:(id<NSFastEnumeration>)signals {
NSMutableArray *copiedSignals = [[NSMutableArray alloc] init];
for (RACSignal *signal in signals) {
[copiedSignals addObject:signal];
}
return [[[RACSignal
createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
for (RACSignal *signal in copiedSignals) {
[subscriber sendNext:signal];
}
[subscriber sendCompleted];
return nil;
}]
flatten]
setNameWithFormat:@"+merge: %@", copiedSignals];
}
現(xiàn)在在回來看這段代碼,copiedSignals雖然是一個(gè)NSMutableArray,但是它近似合成了一個(gè)上圖中的高階信號(hào)拒逮。然后這些信號(hào)們每發(fā)送出來一個(gè)信號(hào)就發(fā)給訂閱者罐氨。整個(gè)操作如flatten的字面意思一樣,壓平滩援。
另外栅隐,在ReactiveCocoa v2.5中,flatten默認(rèn)就是flattenMap:這一種操作狠怨。
public func flatten(_ strategy: FlattenStrategy) -> Signal<Value.Value, Error> {
switch strategy {
case .merge:
return self.merge()
case .concat:
return self.concat()
case .latest:
return self.switchToLatest()
}
}
而在ReactiveCocoa v3.x约啊,v4.x,v5.x中佣赖,flatten的操作是可以選擇3種操作選擇的恰矩。merge,concat憎蛤,switchToLatest外傅。
3. flatten:
flatten:操作也必須是對(duì)高階信號(hào)進(jìn)行操作,如果信號(hào)里面不是信號(hào)俩檬,即不是高階信號(hào)萎胰,那么就會(huì)崩潰。
flatten:的實(shí)現(xiàn)比較復(fù)雜棚辽,一步步的來分析:
- (RACSignal *)flatten:(NSUInteger)maxConcurrent {
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
RACCompoundDisposable *compoundDisposable = [[RACCompoundDisposable alloc] init];
NSMutableArray *activeDisposables = [[NSMutableArray alloc] initWithCapacity:maxConcurrent];
NSMutableArray *queuedSignals = [NSMutableArray array];
__block BOOL selfCompleted = NO;
__block void (^subscribeToSignal)(RACSignal *);
__weak __block void (^recur)(RACSignal *);
recur = subscribeToSignal = ^(RACSignal *signal) { // 暫時(shí)省略};
void (^completeIfAllowed)(void) = ^{ // 暫時(shí)省略};
[compoundDisposable addDisposable:[self subscribeNext:^(RACSignal *signal) {
if (signal == nil) return;
NSCAssert([signal isKindOfClass:RACSignal.class], @"Expected a RACSignal, got %@", signal);
@synchronized (subscriber) {
if (maxConcurrent > 0 && activeDisposables.count >= maxConcurrent) {
[queuedSignals addObject:signal];
return;
}
}
subscribeToSignal(signal);
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
@synchronized (subscriber) {
selfCompleted = YES;
completeIfAllowed();
}
}]];
return compoundDisposable;
}] setNameWithFormat:@"[%@] -flatten: %lu", self.name, (unsigned long)maxConcurrent];
}
先來解釋一些變量技竟,數(shù)組的作用
activeDisposables里面裝的是當(dāng)前正在訂閱的訂閱者們的disposables信號(hào)。
queuedSignals里面裝的是被暫時(shí)緩存起來的信號(hào)屈藐,它們等待被訂閱榔组。
selfCompleted表示高階信號(hào)是否Completed。
subscribeToSignal閉包的作用是訂閱所給的信號(hào)联逻。這個(gè)閉包的入?yún)?shù)就是一個(gè)信號(hào)搓扯,在閉包內(nèi)部訂閱這個(gè)信號(hào),并進(jìn)行一些操作包归。
recur是對(duì)subscribeToSignal閉包的一個(gè)弱引用锨推,防止strong-weak循環(huán)引用,在下面會(huì)分析subscribeToSignal閉包公壤,就會(huì)明白為什么recur要用weak修飾了换可。
completeIfAllowed的作用是在所有信號(hào)都發(fā)送完畢的時(shí)候,通知訂閱者厦幅,給訂閱者發(fā)送completed锦担。
入?yún)axConcurrent的意思是最大可容納同時(shí)被訂閱的信號(hào)個(gè)數(shù)。
再來詳細(xì)分析一下具體訂閱的過程慨削。
flatten:的內(nèi)部洞渔,訂閱高階信號(hào)發(fā)出來的信號(hào)套媚,這部分的代碼比較簡單:
[self subscribeNext:^(RACSignal *signal) {
if (signal == nil) return;
NSCAssert([signal isKindOfClass:RACSignal.class], @"Expected a RACSignal, got %@", signal);
@synchronized (subscriber) {
// 1
if (maxConcurrent > 0 && activeDisposables.count >= maxConcurrent) {
[queuedSignals addObject:signal];
return;
}
}
// 2
subscribeToSignal(signal);
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
@synchronized (subscriber) {
selfCompleted = YES;
// 3
completeIfAllowed();
}
}]];
如果當(dāng)前最大可容納信號(hào)的個(gè)數(shù) > 0 ,且磁椒,activeDisposables數(shù)組里面已經(jīng)裝滿到最大可容納信號(hào)的個(gè)數(shù)堤瘤,不能再裝新的信號(hào)了。那么就把當(dāng)前的信號(hào)緩存到queuedSignals數(shù)組中浆熔。
直到activeDisposables數(shù)組里面有空的位子可以加入新的信號(hào)本辐,那么就調(diào)用subscribeToSignal( )閉包,開始訂閱這個(gè)新的信號(hào)医增。
最后完成的時(shí)候標(biāo)記變量selfCompleted為YES慎皱,并且調(diào)用completeIfAllowed( )閉包。
void (^completeIfAllowed)(void) = ^{
if (selfCompleted && activeDisposables.count == 0) {
[subscriber sendCompleted];
subscribeToSignal = nil;
}
};
當(dāng)selfCompleted = YES 并且activeDisposables數(shù)組里面的信號(hào)都發(fā)送完畢叶骨,沒有可以發(fā)送的信號(hào)了茫多,即activeDisposables.count = 0,那么就給訂閱者sendCompleted忽刽。這里值得一提的是天揖,還需要把subscribeToSignal手動(dòng)置為nil。因?yàn)樵趕ubscribeToSignal閉包中強(qiáng)引用了completeIfAllowed閉包跪帝,防止completeIfAllowed閉包被提早的銷毀掉了今膊。所以在completeIfAllowed閉包執(zhí)行完畢的時(shí)候,需要再把subscribeToSignal閉包置為nil伞剑。
那么接下來需要看的重點(diǎn)就是subscribeToSignal( )閉包斑唬。
recur = subscribeToSignal = ^(RACSignal *signal) {
RACSerialDisposable *serialDisposable = [[RACSerialDisposable alloc] init];
// 1
@synchronized (subscriber) {
[compoundDisposable addDisposable:serialDisposable];
[activeDisposables addObject:serialDisposable];
}
serialDisposable.disposable = [signal subscribeNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
// 2
__strong void (^subscribeToSignal)(RACSignal *) = recur;
RACSignal *nextSignal;
// 3
@synchronized (subscriber) {
[compoundDisposable removeDisposable:serialDisposable];
[activeDisposables removeObjectIdenticalTo:serialDisposable];
// 4
if (queuedSignals.count == 0) {
completeIfAllowed();
return;
}
// 5
nextSignal = queuedSignals[0];
[queuedSignals removeObjectAtIndex:0];
}
// 6
subscribeToSignal(nextSignal);
}];
};
- activeDisposables先添加當(dāng)前高階信號(hào)發(fā)出來的信號(hào)的Disposable( 也就是入?yún)⑿盘?hào)的Disposable)
- 這里會(huì)對(duì)recur進(jìn)行__strong,因?yàn)橄旅娴?步會(huì)用到subscribeToSignal( )閉包黎泣,同樣也是為了防止出現(xiàn)循環(huán)引用赖钞。
- 訂閱入?yún)⑿盘?hào),給訂閱者發(fā)送信號(hào)聘裁。當(dāng)發(fā)送完畢后,activeDisposables中移除它對(duì)應(yīng)的Disposable弓千。
- 如果當(dāng)前緩存的queuedSignals數(shù)組里面沒有緩存的信號(hào)衡便,那么就調(diào)用completeIfAllowed( )閉包。
- 如果當(dāng)前緩存的queuedSignals數(shù)組里面有緩存的信號(hào)洋访,那么就取出第0個(gè)信號(hào)镣陕,并在queuedSignals數(shù)組移除它。
- 把第4步取出的信號(hào)繼續(xù)訂閱姻政,繼續(xù)調(diào)用subscribeToSignal( )閉包呆抑。
總結(jié)一下:高階信號(hào)每發(fā)送一個(gè)信號(hào)值,判斷activeDisposables數(shù)組裝的個(gè)數(shù)是否已經(jīng)超過了maxConcurrent汁展。如果裝不下了就緩存進(jìn)queuedSignals數(shù)組中鹊碍。如果還可以裝的下就開始調(diào)用subscribeToSignal( )閉包厌殉,訂閱當(dāng)前信號(hào)。
每發(fā)送完一個(gè)信號(hào)就判斷緩存數(shù)組queuedSignals的個(gè)數(shù)侈咕,如果緩存數(shù)組里面已經(jīng)沒有信號(hào)了公罕,那么就結(jié)束原來高階信號(hào)的發(fā)送。如果緩存數(shù)組里面還有信號(hào)就繼續(xù)訂閱耀销。如此循環(huán)楼眷,直到原高階信號(hào)所有的信號(hào)都發(fā)送完畢。
整個(gè)flatten:的執(zhí)行流程都分析清楚了熊尉,最后罐柳,關(guān)于入?yún)axConcurrent進(jìn)行更進(jìn)一步的解讀。
回看上面flatten:的實(shí)現(xiàn)中有這樣一句話:
if (maxConcurrent > 0 && activeDisposables.count >= maxConcurrent)
那么maxConcurrent的值域就是最終決定flatten:表現(xiàn)行為狰住。
如果maxConcurrent < 0张吉,會(huì)發(fā)生什么?程序會(huì)崩潰转晰。因?yàn)樵谠创a中有這樣一行的初始化的代碼:
NSMutableArray *activeDisposables = [[NSMutableArray alloc] initWithCapacity:maxConcurrent];
activeDisposables在初始化的時(shí)候會(huì)初始化一個(gè)大小為maxConcurrent的NSMutableArray芦拿。如果maxConcurrent < 0,那么這里初始化就會(huì)崩潰查邢。
如果maxConcurrent = 0蔗崎,會(huì)發(fā)生什么?那么flatten:就退化成flatten了扰藕。
如果maxConcurrent = 1缓苛,會(huì)發(fā)生什么?那么flatten:就退化成concat了邓深。
如果maxConcurrent > 1未桥,會(huì)發(fā)生什么?由于至今還沒有遇到能用到maxConcurrent > 1的需求情況芥备,所以這里暫時(shí)不展示圖解了冬耿。maxConcurrent > 1之后,flatten的行為還依照高階信號(hào)的個(gè)數(shù)和maxConcurrent的關(guān)系萌壳。如果高階信號(hào)的個(gè)數(shù)<=maxConcurrent的值亦镶,那么flatten:又退化成flatten了。如果高階信號(hào)的個(gè)數(shù)>maxConcurrent的值袱瓮,那么多的信號(hào)就會(huì)進(jìn)入queuedSignals緩存數(shù)組缤骨。
4. concat
這里的concat實(shí)現(xiàn)是在RACSignal里面定義的。
- (RACSignal *)concat {
return [[self flatten:1] setNameWithFormat:@"[%@] -concat", self.name];
}
一看源碼就知道了尺借,concat其實(shí)就是flatten:1绊起。
當(dāng)然在RACSignal中定義了concat:方法,這個(gè)方法在之前的文章已經(jīng)分析過了燎斩,這里回顧對(duì)比一下:
- (RACSignal *)concat:(RACSignal *)signal {
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
RACSerialDisposable *serialDisposable = [[RACSerialDisposable alloc] init];
RACDisposable *sourceDisposable = [self subscribeNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
RACDisposable *concattedDisposable = [signal subscribe:subscriber];
serialDisposable.disposable = concattedDisposable;
}];
serialDisposable.disposable = sourceDisposable;
return serialDisposable;
}] setNameWithFormat:@"[%@] -concat: %@", self.name, signal];
}
經(jīng)過對(duì)比可以發(fā)現(xiàn)虱歪,雖然最終變換出來的結(jié)果類似蜂绎,但是針對(duì)的信號(hào)的對(duì)象是不同的,concat是針對(duì)高階信號(hào)進(jìn)行降階操作实蔽。concat:是把兩個(gè)信號(hào)連接起來的操作荡碾。如果把高階信號(hào)按照時(shí)間軸,從左往右局装,依次把每個(gè)信號(hào)都concat:連接起來坛吁,那么結(jié)果就是concat。
5. switchToLatest
- (RACSignal *)switchToLatest {
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
RACMulticastConnection *connection = [self publish];
RACDisposable *subscriptionDisposable = [[connection.signal
flattenMap:^(RACSignal *x) {
NSCAssert(x == nil || [x isKindOfClass:RACSignal.class], @"-switchToLatest requires that the source signal (%@) send signals. Instead we got: %@", self, x);
return [x takeUntil:[connection.signal concat:[RACSignal never]]];
}]
subscribe:subscriber];
RACDisposable *connectionDisposable = [connection connect];
return [RACDisposable disposableWithBlock:^{
[subscriptionDisposable dispose];
[connectionDisposable dispose];
}];
}] setNameWithFormat:@"[%@] -switchToLatest", self.name];
}
switchToLatest這個(gè)操作只能用在高階信號(hào)上铐尚,如果原信號(hào)里面有不是信號(hào)的值拨脉,那么就會(huì)崩潰,崩潰信息如下:
***** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-switchToLatest requires that the source signal (<RACDynamicSignal: 0x608000038ec0> name: ) send signals.
在switchToLatest操作中宣增,先把原信號(hào)轉(zhuǎn)換成熱信號(hào)玫膀,connection.signal就是RACSubject類型的。對(duì)RACSubject進(jìn)行flattenMap:變換爹脾。在flattenMap:變換中帖旨,connection.signal會(huì)先concat:一個(gè)never信號(hào)。這里concat:一個(gè)never信號(hào)的原因是為了內(nèi)部的信號(hào)過早的結(jié)束而導(dǎo)致訂閱者收到complete信號(hào)灵妨。
flattenMap:變換中x也是一個(gè)信號(hào)解阅,對(duì)x進(jìn)行takeUntil:變換,效果就是下一個(gè)信號(hào)到來之前泌霍,x會(huì)一直發(fā)送信號(hào)货抄,一旦下一個(gè)信號(hào)到來,x就會(huì)被取消訂閱朱转,開始訂閱新的信號(hào)蟹地。
一個(gè)高階信號(hào)經(jīng)過switchToLatest降階操作之后,能得到上圖中的信號(hào)藤为。
6. switch: cases: default:
switch: cases: default:源碼實(shí)現(xiàn)如下:
+ (RACSignal *)switch:(RACSignal *)signal cases:(NSDictionary *)cases default:(RACSignal *)defaultSignal {
NSCParameterAssert(signal != nil);
NSCParameterAssert(cases != nil);
for (id key in cases) {
id value __attribute__((unused)) = cases[key];
NSCAssert([value isKindOfClass:RACSignal.class], @"Expected all cases to be RACSignals, %@ isn't", value);
}
NSDictionary *copy = [cases copy];
return [[[signal
map:^(id key) {
if (key == nil) key = RACTupleNil.tupleNil;
RACSignal *signal = copy[key] ?: defaultSignal;
if (signal == nil) {
NSString *description = [NSString stringWithFormat:NSLocalizedString(@"No matching signal found for value %@", @""), key];
return [RACSignal error:[NSError errorWithDomain:RACSignalErrorDomain code:RACSignalErrorNoMatchingCase userInfo:@{ NSLocalizedDescriptionKey: description }]];
}
return signal;
}]
switchToLatest]
setNameWithFormat:@"+switch: %@ cases: %@ default: %@", signal, cases, defaultSignal];
}
實(shí)現(xiàn)中有3個(gè)斷言怪与,全部都是針對(duì)入?yún)⒌囊蟆H雲(yún)ignal信號(hào)和cases字典都不能是nil缅疟。其次分别,cases字典里面所有key對(duì)應(yīng)的value必須是RACSignal類型的。注意窿吩,defaultSignal是可以為nil的。
接下來的實(shí)現(xiàn)比較簡單错览,對(duì)入?yún)鬟M(jìn)來的signal信號(hào)進(jìn)行map變換纫雁,這里的變換是升階的變換。
signal每次發(fā)送出來的一個(gè)值倾哺,就把這個(gè)值當(dāng)做key值去cases字典里面去查找對(duì)應(yīng)的value轧邪。當(dāng)然value對(duì)應(yīng)的是一個(gè)信號(hào)刽脖。如果value對(duì)應(yīng)的信號(hào)不為空,就把signal發(fā)送出來的這個(gè)值map成字典里面對(duì)應(yīng)的信號(hào)忌愚。如果value對(duì)應(yīng)為空曲管,那么就把原signal發(fā)出來的值map成defaultSignal信號(hào)。
如果經(jīng)過轉(zhuǎn)換之后硕糊,得到的信號(hào)為nil院水,就會(huì)返回一個(gè)error信號(hào)。如果得到的信號(hào)不為nil简十,那么原信號(hào)完全轉(zhuǎn)換完成就會(huì)變成一個(gè)高階信號(hào)檬某,這個(gè)高階信號(hào)里面裝的都是信號(hào)。最后再對(duì)這個(gè)高階信號(hào)執(zhí)行switchToLatest轉(zhuǎn)換螟蝙。
7. if: then: else:
if: then: else:源碼實(shí)現(xiàn)如下:
+ (RACSignal *)if:(RACSignal *)boolSignal then:(RACSignal *)trueSignal else:(RACSignal *)falseSignal {
NSCParameterAssert(boolSignal != nil);
NSCParameterAssert(trueSignal != nil);
NSCParameterAssert(falseSignal != nil);
return [[[boolSignal
map:^(NSNumber *value) {
NSCAssert([value isKindOfClass:NSNumber.class], @"Expected %@ to send BOOLs, not %@", boolSignal, value);
return (value.boolValue ? trueSignal : falseSignal);
}]
switchToLatest]
setNameWithFormat:@"+if: %@ then: %@ else: %@", boolSignal, trueSignal, falseSignal];
}
入?yún)oolSignal恢恼,trueSignal,falseSignal三個(gè)信號(hào)都不能為nil胰默。
boolSignal里面都必須裝的是NSNumber類型的值场斑。
針對(duì)boolSignal進(jìn)行map升階操作,boolSignal信號(hào)里面的值如果是YES牵署,那么就轉(zhuǎn)換成trueSignal信號(hào)漏隐,如果為NO,就轉(zhuǎn)換成falseSignal碟刺。升階轉(zhuǎn)換完成之后锁保,boolSignal就是一個(gè)高階信號(hào),然后再進(jìn)行switchToLatest操作半沽。
8. catch:
catch:的實(shí)現(xiàn)如下:
- (RACSignal *)catch:(RACSignal * (^)(NSError *error))catchBlock {
NSCParameterAssert(catchBlock != NULL);
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
RACSerialDisposable *catchDisposable = [[RACSerialDisposable alloc] init];
RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
RACSignal *signal = catchBlock(error);
NSCAssert(signal != nil, @"Expected non-nil signal from catch block on %@", self);
catchDisposable.disposable = [signal subscribe:subscriber];
} completed:^{
[subscriber sendCompleted];
}];
return [RACDisposable disposableWithBlock:^{
[catchDisposable dispose];
[subscriptionDisposable dispose];
}];
}] setNameWithFormat:@"[%@] -catch:", self.name];
}
當(dāng)對(duì)原信號(hào)進(jìn)行訂閱的時(shí)候爽柒,如果出現(xiàn)了錯(cuò)誤,會(huì)去執(zhí)行catchBlock( )閉包者填,入?yún)閯倓偖a(chǎn)生的error浩村。catchBlock( )閉包產(chǎn)生的是一個(gè)新的RACSignal,并再次用訂閱者訂閱該信號(hào)占哟。
這里之所以說是高階操作心墅,是因?yàn)檫@里原信號(hào)發(fā)生錯(cuò)誤之后,錯(cuò)誤會(huì)升階成一個(gè)信號(hào)榨乎。
9. catchTo:
catchTo:的實(shí)現(xiàn)如下:
- (RACSignal *)catchTo:(RACSignal *)signal {
return [[self catch:^(NSError *error) {
return signal;
}] setNameWithFormat:@"[%@] -catchTo: %@", self.name, signal];
}
catchTo:的實(shí)現(xiàn)就是調(diào)用catch:方法怎燥,只不過原來catch:方法里面的catchBlock( )閉包,永遠(yuǎn)都只返回catchTo:的入?yún)⒚凼睿瑂ignal信號(hào)铐姚。
10. try:
- (RACSignal *)try:(BOOL (^)(id value, NSError **errorPtr))tryBlock {
NSCParameterAssert(tryBlock != NULL);
return [[self flattenMap:^(id value) {
NSError *error = nil;
BOOL passed = tryBlock(value, &error);
return (passed ? [RACSignal return:value] : [RACSignal error:error]);
}] setNameWithFormat:@"[%@] -try:", self.name];
}
try:可以用來進(jìn)來信號(hào)的升階操作。對(duì)原信號(hào)進(jìn)行flattenMap變換肛捍,對(duì)信號(hào)發(fā)出來的每個(gè)值都調(diào)用一遍tryBlock( )閉包隐绵,如果這個(gè)閉包的返回值是YES之众,那么就返回[RACSignal return:value],如果閉包的返回值是NO依许,那么就返回error棺禾。原信號(hào)中如果都是值,那么經(jīng)過try:操作之后峭跳,每個(gè)值都會(huì)變成RACSignal膘婶,于是原信號(hào)也就變成了高階信號(hào)了。
當(dāng)然坦康,如果在block的實(shí)現(xiàn)中返回一個(gè)信號(hào)竣付,這時(shí)就不會(huì)升階了。返回的信號(hào)里面可以不返回信號(hào)滞欠,而是直接返回值古胆。
11. tryMap:
- (RACSignal *)tryMap:(id (^)(id value, NSError **errorPtr))mapBlock {
NSCParameterAssert(mapBlock != NULL);
return [[self flattenMap:^(id value) {
NSError *error = nil;
id mappedValue = mapBlock(value, &error);
return (mappedValue == nil ? [RACSignal error:error] : [RACSignal return:mappedValue]);
}] setNameWithFormat:@"[%@] -tryMap:", self.name];
}
tryMap:的實(shí)現(xiàn)和try:的實(shí)現(xiàn)基本一致,唯一不同的就是入?yún)㈤]包的返回值不同筛璧。在tryMap:中調(diào)用mapBlock( )閉包逸绎,返回是一個(gè)對(duì)象,如果這個(gè)對(duì)象不為nil夭谤,就返回[RACSignal return:mappedValue]棺牧。如果返回的對(duì)象是nil,那么就變換成error信號(hào)朗儒。
12. timeout: onScheduler:
- (RACSignal *)timeout:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler {
NSCParameterAssert(scheduler != nil);
NSCParameterAssert(scheduler != RACScheduler.immediateScheduler);
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
RACDisposable *timeoutDisposable = [scheduler afterDelay:interval schedule:^{
[disposable dispose];
[subscriber sendError:[NSError errorWithDomain:RACSignalErrorDomain code:RACSignalErrorTimedOut userInfo:nil]];
}];
[disposable addDisposable:timeoutDisposable];
RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
[disposable dispose];
[subscriber sendError:error];
} completed:^{
[disposable dispose];
[subscriber sendCompleted];
}];
[disposable addDisposable:subscriptionDisposable];
return disposable;
}] setNameWithFormat:@"[%@] -timeout: %f onScheduler: %@", self.name, (double)interval, scheduler];
}
timeout: onScheduler:的實(shí)現(xiàn)很簡單颊乘,它比正常的信號(hào)訂閱多了一個(gè)timeoutDisposable操作。它在信號(hào)訂閱的內(nèi)部開啟了一個(gè)scheduler醉锄,經(jīng)過interval的時(shí)間之后乏悄,就會(huì)停止訂閱原信號(hào),并對(duì)訂閱者sendError恳不。
這個(gè)操作的表意和方法名完全一致檩小,經(jīng)過interval的時(shí)間之后,就算timeout烟勋,那么就停止訂閱原信號(hào)规求,并sendError。
總結(jié)一下ReactiveCocoa v2.5中高階信號(hào)的升階 / 降階操作:
升階操作:
- map( 把值map成一個(gè)信號(hào))
- [RACSignal return:signal]
降階操作:
- flatten(等效于flatten:0卵惦,+merge:)
- concat(等效于flatten:1)
- flatten:1
- switchToLatest
- flattenMap:
這5種操作能將高階信號(hào)變?yōu)榈碗A信號(hào)阻肿,但是最終降階之后的效果就只有3種:switchToLatest,flatten沮尿,concat丛塌。具體的圖示見上面的分析。
二. 同步操作
在ReactiveCocoa中還包含一些同步的操作,這些操作一般我們很少使用姨伤,除非真的很確定這樣做了之后不會(huì)有什么問題,否則胡亂使用會(huì)導(dǎo)致線程死鎖等一些嚴(yán)重的問題庸疾。
1. firstOrDefault: success: error:
- (id)firstOrDefault:(id)defaultValue success:(BOOL *)success error:(NSError **)error {
NSCondition *condition = [[NSCondition alloc] init];
condition.name = [NSString stringWithFormat:@"[%@] -firstOrDefault: %@ success:error:", self.name, defaultValue];
__block id value = defaultValue;
__block BOOL done = NO;
// Ensures that we don't pass values across thread boundaries by reference.
__block NSError *localError;
__block BOOL localSuccess;
[[self take:1] subscribeNext:^(id x) {
// 加鎖
[condition lock];
value = x;
localSuccess = YES;
done = YES;
[condition broadcast];
// 解鎖
[condition unlock];
} error:^(NSError *e) {
// 加鎖
[condition lock];
if (!done) {
localSuccess = NO;
localError = e;
done = YES;
[condition broadcast];
}
// 解鎖
[condition unlock];
} completed:^{
// 加鎖
[condition lock];
localSuccess = YES;
done = YES;
[condition broadcast];
// 解鎖
[condition unlock];
}];
// 加鎖
[condition lock];
while (!done) {
[condition wait];
}
if (success != NULL) *success = localSuccess;
if (error != NULL) *error = localError;
// 解鎖
[condition unlock];
return value;
}
從源碼上看乍楚,firstOrDefault: success: error:這種同步的方法很容易導(dǎo)致線程死鎖胡岔。它在subscribeNext衙传,error习柠,completed的閉包里面都調(diào)用condition鎖先lock再unlock鸟妙。如果一個(gè)信號(hào)發(fā)送值過來国瓮,都沒有執(zhí)行subscribeNext醒颖,error走哺,completed這3個(gè)操作里面的任意一個(gè)坏平,那么就會(huì)執(zhí)行[condition wait]揍拆,等待渠概。
由于對(duì)原信號(hào)進(jìn)行了take:1操作,所以只會(huì)對(duì)第一個(gè)值進(jìn)行操作嫂拴。執(zhí)行完subscribeNext播揪,error,completed這3個(gè)操作里面的任意一個(gè)筒狠,又會(huì)加一次鎖猪狈,對(duì)外部傳進(jìn)來的入?yún)uccess和error進(jìn)行賦值,已便外部可以拿到里面的狀態(tài)辩恼。最終返回信號(hào)是原信號(hào)中第一個(gè)next里面的值雇庙,如果原信號(hào)第一個(gè)值沒有,比如直接error或者completed灶伊,那么返回的是defaultValue疆前。
done為YES表示已經(jīng)成功執(zhí)行了subscribeNext,error谁帕,completed這3個(gè)操作里面的任意一個(gè)峡继。反之為NO。
localSuccess為YES表示成功發(fā)送值或者成功發(fā)送完了原信號(hào)的所有值匈挖,期間沒有發(fā)生錯(cuò)誤碾牌。
condition的broadcast操作是喚醒其他線程的操作,相當(dāng)于操作系統(tǒng)里面互斥信號(hào)量的signal操作儡循。
入?yún)efaultValue是給內(nèi)部變量value的一個(gè)初始值舶吗。當(dāng)原信號(hào)發(fā)送出一個(gè)值之后,value的值時(shí)刻都會(huì)與原信號(hào)的值保持一致择膝。
success和error是外部變量的地址誓琼,從外面可以監(jiān)聽到里面的狀態(tài)。在函數(shù)內(nèi)部賦值,在函數(shù)外面拿到它們的值腹侣。
2. firstOrDefault:
- (id)firstOrDefault:(id)defaultValue {
return [self firstOrDefault:defaultValue success:NULL error:NULL];
}
firstOrDefault:的實(shí)現(xiàn)就是調(diào)用了firstOrDefault: success: error:方法叔收。只不過不需要傳success和error,不關(guān)心內(nèi)部的狀態(tài)傲隶。最終返回信號(hào)是原信號(hào)中第一個(gè)next里面的值饺律,如果原信號(hào)第一個(gè)值沒有,比如直接error或者completed跺株,那么返回的是defaultValue复濒。
3. first
- (id)first {
return [self firstOrDefault:nil];
}
first方法就更加省略,連defaultValue也不傳乒省。最終返回信號(hào)是原信號(hào)中第一個(gè)next里面的值巧颈,如果原信號(hào)第一個(gè)值沒有,比如直接error或者completed袖扛,那么返回的是nil砸泛。
4. waitUntilCompleted:
- (BOOL)waitUntilCompleted:(NSError **)error {
BOOL success = NO;
[[[self
ignoreValues]
setNameWithFormat:@"[%@] -waitUntilCompleted:", self.name]
firstOrDefault:nil success:&success error:error];
return success;
}
waitUntilCompleted:里面還是調(diào)用firstOrDefault: success: error:方法。返回值是success蛆封。只要原信號(hào)正常的發(fā)送完信號(hào)晾嘶,success應(yīng)該為YES,但是如果發(fā)送過程中出現(xiàn)了error娶吞,success就為NO垒迂。success作為返回值,外部就可以監(jiān)聽到是否發(fā)送成功妒蛇。
雖然這個(gè)方法可以監(jiān)聽到發(fā)送結(jié)束的狀態(tài)机断,但是也盡量不要使用,因?yàn)樗膶?shí)現(xiàn)調(diào)用了firstOrDefault: success: error:方法绣夺,這個(gè)方法里面有大量的鎖的操作吏奸,一不留神就會(huì)導(dǎo)致死鎖。
5. toArray
- (NSArray *)toArray {
return [[[self collect] first] copy];
}
經(jīng)過collect之后陶耍,原信號(hào)所有的值都會(huì)被加到一個(gè)數(shù)組里面奋蔚,取出信號(hào)的第一個(gè)值就是一個(gè)數(shù)組。所以執(zhí)行完first之后第一個(gè)值就是原信號(hào)所有值的數(shù)組烈钞。
三. 副作用操作
ReactiveCocoa v2.5中還為我們提供了一些可以進(jìn)行副作用操作的函數(shù)泊碑。
1. doNext:
- (RACSignal *)doNext:(void (^)(id x))block {
NSCParameterAssert(block != NULL);
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
return [self subscribeNext:^(id x) {
block(x);
[subscriber sendNext:x];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
[subscriber sendCompleted];
}];
}] setNameWithFormat:@"[%@] -doNext:", self.name];
}
doNext:能讓我們?cè)谠盘?hào)sendNext之前,能執(zhí)行一個(gè)block閉包毯欣,在這個(gè)閉包中我們可以執(zhí)行我們想要執(zhí)行的副作用操作馒过。
2. doError:
- (RACSignal *)doError:(void (^)(NSError *error))block {
NSCParameterAssert(block != NULL);
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
return [self subscribeNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
block(error);
[subscriber sendError:error];
} completed:^{
[subscriber sendCompleted];
}];
}] setNameWithFormat:@"[%@] -doError:", self.name];
}
doError:能讓我們?cè)谠盘?hào)sendError之前,能執(zhí)行一個(gè)block閉包酗钞,在這個(gè)閉包中我們可以執(zhí)行我們想要執(zhí)行的副作用操作腹忽。
3. doCompleted:
- (RACSignal *)doCompleted:(void (^)(void))block {
NSCParameterAssert(block != NULL);
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
return [self subscribeNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
block();
[subscriber sendCompleted];
}];
}] setNameWithFormat:@"[%@] -doCompleted:", self.name];
}
doCompleted:能讓我們?cè)谠盘?hào)sendCompleted之前来累,能執(zhí)行一個(gè)block閉包,在這個(gè)閉包中我們可以執(zhí)行我們想要執(zhí)行的副作用操作窘奏。
4. initially:
- (RACSignal *)initially:(void (^)(void))block {
NSCParameterAssert(block != NULL);
return [[RACSignal defer:^{
block();
return self;
}] setNameWithFormat:@"[%@] -initially:", self.name];
}
initially:能讓我們?cè)谠盘?hào)發(fā)送之前嘹锁,先調(diào)用了defer:操作,在return self之前先執(zhí)行了一個(gè)閉包着裹,在這個(gè)閉包中我們可以執(zhí)行我們想要執(zhí)行的副作用操作兼耀。
5. finally:
- (RACSignal *)finally:(void (^)(void))block {
NSCParameterAssert(block != NULL);
return [[[self
doError:^(NSError *error) {
block();
}]
doCompleted:^{
block();
}]
setNameWithFormat:@"[%@] -finally:", self.name];
}
finally:操作調(diào)用了doError:和doCompleted:操作,依次在sendError之前求冷,sendCompleted之前,插入一個(gè)block( )閉包窍霞。這樣當(dāng)信號(hào)因?yàn)殄e(cuò)誤而要終止取消訂閱匠题,或者,發(fā)送結(jié)束之前但金,都能執(zhí)行一段我們想要執(zhí)行的副作用操作韭山。
四. 多線程操作
在RACSignal里面有3個(gè)關(guān)于多線程的操作。
1. deliverOn:
- (RACSignal *)deliverOn:(RACScheduler *)scheduler {
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
return [self subscribeNext:^(id x) {
[scheduler schedule:^{
[subscriber sendNext:x];
}];
} error:^(NSError *error) {
[scheduler schedule:^{
[subscriber sendError:error];
}];
} completed:^{
[scheduler schedule:^{
[subscriber sendCompleted];
}];
}];
}] setNameWithFormat:@"[%@] -deliverOn: %@", self.name, scheduler];
}
deliverOn:的入?yún)⑹且粋€(gè)scheduler冷溃,當(dāng)原信號(hào)subscribeNext钱磅,sendError,sendCompleted的時(shí)候似枕,都去調(diào)用scheduler的schedule方法盖淡。
- (RACDisposable *)schedule:(void (^)(void))block {
NSCParameterAssert(block != NULL);
if (RACScheduler.currentScheduler == nil) return [self.backgroundScheduler schedule:block];
block();
return nil;
}
在schedule的方法里面會(huì)判斷當(dāng)前currentScheduler是否為nil,如果是nil就調(diào)用backgroundScheduler去執(zhí)行block( )閉包,如果不為nil凿歼,當(dāng)前currentScheduler直接執(zhí)行block( )閉包褪迟。
+ (instancetype)currentScheduler {
RACScheduler *scheduler = NSThread.currentThread.threadDictionary[RACSchedulerCurrentSchedulerKey];
if (scheduler != nil) return scheduler;
if ([self.class isOnMainThread]) return RACScheduler.mainThreadScheduler;
return nil;
}
判斷currentScheduler是否存在,看兩點(diǎn)答憔,一是當(dāng)前線程的字典里面味赃,是否存在RACSchedulerCurrentSchedulerKey( @"RACSchedulerCurrentSchedulerKey" ),如果存在對(duì)應(yīng)的value虐拓,返回scheduler心俗,二是看當(dāng)前的類是不是在主線程,如果在主線程蓉驹,返回mainThreadScheduler城榛。如果兩個(gè)條件都不存在,那么當(dāng)前currentScheduler就不存在态兴,返回nil吠谢。
deliverOn:操作的特點(diǎn)是原信號(hào)發(fā)送sendNext,sendError诗茎,sendCompleted所在線程是確定的工坊。
2. subscribeOn:
- (RACSignal *)subscribeOn:(RACScheduler *)scheduler {
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
RACDisposable *schedulingDisposable = [scheduler schedule:^{
RACDisposable *subscriptionDisposable = [self subscribe:subscriber];
[disposable addDisposable:subscriptionDisposable];
}];
[disposable addDisposable:schedulingDisposable];
return disposable;
}] setNameWithFormat:@"[%@] -subscribeOn: %@", self.name, scheduler];
}
subscribeOn:操作就是在傳入的scheduler的閉包內(nèi)部訂閱原信號(hào)的献汗。它與deliverOn:操作就不同:
subscribeOn:操作能夠保證didSubscribe block( )閉包在入?yún)cheduler中執(zhí)行,但是不能保證原信號(hào)subscribeNext王污,sendError罢吃,sendCompleted在哪個(gè)scheduler中執(zhí)行。
deliverOn:與subscribeOn:正好反過來昭齐,能保證原信號(hào)subscribeNext尿招,sendError,sendCompleted在哪個(gè)scheduler中執(zhí)行阱驾,但是不能保證didSubscribe block( )閉包在哪個(gè)scheduler中執(zhí)行就谜。
3. deliverOnMainThread
- (RACSignal *)deliverOnMainThread {
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
__block volatile int32_t queueLength = 0;
void (^performOnMainThread)(dispatch_block_t) = ^(dispatch_block_t block) { // 暫時(shí)省略};
return [self subscribeNext:^(id x) {
performOnMainThread(^{
[subscriber sendNext:x];
});
} error:^(NSError *error) {
performOnMainThread(^{
[subscriber sendError:error];
});
} completed:^{
performOnMainThread(^{
[subscriber sendCompleted];
});
}];
}] setNameWithFormat:@"[%@] -deliverOnMainThread", self.name];
}
對(duì)比deliverOn:的源碼實(shí)現(xiàn),發(fā)現(xiàn)兩者比較相似里覆,只不過這里deliverOnMainThread把sendNext丧荐,sendError,sendCompleted都包在了performOnMainThread閉包中執(zhí)行喧枷。
__block volatile int32_t queueLength = 0;
void (^performOnMainThread)(dispatch_block_t) = ^(dispatch_block_t block) {
int32_t queued = OSAtomicIncrement32(&queueLength);
if (NSThread.isMainThread && queued == 1) {
block();
OSAtomicDecrement32(&queueLength);
} else {
dispatch_async(dispatch_get_main_queue(), ^{
block();
OSAtomicDecrement32(&queueLength);
});
}
};
performOnMainThread閉包內(nèi)部保證了入?yún)lock( )閉包一定是在主線程中執(zhí)行虹统。
OSAtomicIncrement32 和 OSAtomicDecrement32是原子操作,分別代表+1和-1隧甚。下面的if-else判斷里面车荔,不管是滿足哪一條,最終都還是在主線程中執(zhí)行block( )閉包戚扳。
deliverOnMainThread能保證原信號(hào)subscribeNext忧便,sendError,sendCompleted都在主線程MainThread中執(zhí)行帽借。
五. 其他操作
1. setKeyPath: onObject: nilValue:
setKeyPath: onObject: nilValue: 的源碼實(shí)現(xiàn)如下:
- (RACDisposable *)setKeyPath:(NSString *)keyPath onObject:(NSObject *)object nilValue:(id)nilValue {
NSCParameterAssert(keyPath != nil);
NSCParameterAssert(object != nil);
keyPath = [keyPath copy];
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
__block void * volatile objectPtr = (__bridge void *)object;
RACDisposable *subscriptionDisposable = [self subscribeNext:^(id x) {
// 1
__strong NSObject *object __attribute__((objc_precise_lifetime)) = (__bridge __strong id)objectPtr;
[object setValue:x ?: nilValue forKeyPath:keyPath];
} error:^(NSError *error) {
__strong NSObject *object __attribute__((objc_precise_lifetime)) = (__bridge __strong id)objectPtr;
NSCAssert(NO, @"Received error from %@ in binding for key path \"%@\" on %@: %@", self, keyPath, object, error);
NSLog(@"Received error from %@ in binding for key path \"%@\" on %@: %@", self, keyPath, object, error);
[disposable dispose];
} completed:^{
[disposable dispose];
}];
[disposable addDisposable:subscriptionDisposable];
#if DEBUG
static void *bindingsKey = &bindingsKey;
NSMutableDictionary *bindings;
@synchronized (object) {
// 2
bindings = objc_getAssociatedObject(object, bindingsKey);
if (bindings == nil) {
bindings = [NSMutableDictionary dictionary];
objc_setAssociatedObject(object, bindingsKey, bindings, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
@synchronized (bindings) {
NSCAssert(bindings[keyPath] == nil, @"Signal %@ is already bound to key path \"%@\" on object %@, adding signal %@ is undefined behavior", [bindings[keyPath] nonretainedObjectValue], keyPath, object, self);
bindings[keyPath] = [NSValue valueWithNonretainedObject:self];
}
#endif
RACDisposable *clearPointerDisposable = [RACDisposable disposableWithBlock:^{
#if DEBUG
@synchronized (bindings) {
// 3
[bindings removeObjectForKey:keyPath];
}
#endif
while (YES) {
void *ptr = objectPtr;
// 4
if (OSAtomicCompareAndSwapPtrBarrier(ptr, NULL, &objectPtr)) {
break;
}
}
}];
[disposable addDisposable:clearPointerDisposable];
[object.rac_deallocDisposable addDisposable:disposable];
RACCompoundDisposable *objectDisposable = object.rac_deallocDisposable;
return [RACDisposable disposableWithBlock:^{
[objectDisposable removeDisposable:disposable];
[disposable dispose];
}];
}
代碼雖然有點(diǎn)長茬腿,但是逐行讀下來不是很難,需要注意的有4點(diǎn)地方宜雀,已經(jīng)在上述代碼里面標(biāo)明了切平。接下來一一分析。
1. objc_precise_lifetime的問題辐董。
作者在這里寫了一段注釋:
Possibly spec, possibly compiler bug, but this __bridge cast does not result in a retain here, effectively an invisible __unsafe_unretained qualifier. Using objc_precise_lifetime gives the __strong reference desired. The explicit use of __strong is strictly defensive.
作者懷疑是編譯器的一個(gè)bug悴品,即使是顯示的調(diào)用了__strong,依舊沒法保證被強(qiáng)引用了简烘,所以還需要用objc_precise_lifetime來保證強(qiáng)引用苔严。
關(guān)于這個(gè)問題,筆者查詢了一下LLVM的文檔孤澎,在6.3 precise lifetime semantics這一節(jié)中提到了這個(gè)問題届氢。
通常上,凡是聲明了__strong的變量覆旭,都會(huì)有很確切的生命周期退子。ARC會(huì)維持這些__strong的變量在其生命周期中被retained岖妄。
但是自動(dòng)存儲(chǔ)的局部變量是沒有確切的生命周期的。這些變量僅僅只是簡單的持有一個(gè)強(qiáng)引用寂祥,強(qiáng)引用著retain對(duì)象的指針類型的值荐虐。這些值完全受控于本地控制者的如何優(yōu)化。所以要想改變這些局部變量的生命周期丸凭,是不可能的事情福扬。因?yàn)橛刑嗟膬?yōu)化,理論上都會(huì)導(dǎo)致局部變量的生命周期減少惜犀,但是這些優(yōu)化非常有用铛碑。
但是LLVM為我們提供了一個(gè)關(guān)鍵字objc_precise_lifetime,使用這個(gè)可以是局部變量的生命周期變成確切的虽界。這個(gè)關(guān)鍵字有時(shí)候還是非常有用的汽烦。甚至更加極端情況,該局部變量都沒有被使用浓恳,但是它依舊可以保持一個(gè)確定的生命周期。
回到源碼上來碗暗,接著代碼會(huì)對(duì)入?yún)bject進(jìn)行setValue: forKeyPath:
[object setValue:x ?: nilValue forKeyPath:keyPath];
如何x為nil就返回nilValue傳進(jìn)來的值颈将。
2. AssociatedObject關(guān)聯(lián)對(duì)象
如果bindings字典不存在,那么就調(diào)用objc_setAssociatedObject對(duì)object進(jìn)行關(guān)聯(lián)對(duì)象言疗。參數(shù)是OBJC_ASSOCIATION_RETAIN_NONATOMIC晴圾。如果bindings字典存在,就用objc_getAssociatedObject取出字典噪奄。
在字典里面重新更新綁定key-value值死姚,key就是入?yún)eyPath,value是原信號(hào)勤篮。
3. 取消訂閱原信號(hào)的時(shí)候
[bindings removeObjectForKey:keyPath];
當(dāng)信號(hào)取消訂閱的時(shí)候都毒,移除所有的關(guān)聯(lián)值。
3. OSAtomicCompareAndSwapPtrBarrier
這個(gè)函數(shù)屬于OSAtomic原子操作碰缔,原型如下:
OSAtomicCompareAndSwapPtrBarrier(type __oldValue, type __newValue, volatile type *__theValue)
Compares a variable against the specified old value. If the two values are equal, this function assigns the specified new value to the variable; otherwise, it does nothing. The comparison and assignment are done as one atomic operation and the function returns a Boolean value indicating whether the swap actually occurred.
這個(gè)函數(shù)用于比較__oldValue是否與__theValue指針指向的內(nèi)存位置的值匹配账劲,如果匹配,則將__newValue的值存儲(chǔ)到__theValue指向的內(nèi)存位置金抡。整個(gè)函數(shù)的返回值就是交換是否成功的BOOL值瀑焦。
while (YES) {
void *ptr = objectPtr;
if (OSAtomicCompareAndSwapPtrBarrier(ptr, NULL, &objectPtr)) {
break;
}
}
在這個(gè)while的死循環(huán)里面只有當(dāng)OSAtomicCompareAndSwapPtrBarrier返回值為YES,才能退出整個(gè)死循環(huán)梗肝。返回值為YES就代表&objectPtr被置為了NULL榛瓮,這樣就確保了在線程安全的情況下,不存在野指針的問題了巫击。
2. setKeyPath: onObject:
- (RACDisposable *)setKeyPath:(NSString *)keyPath onObject:(NSObject *)object {
return [self setKeyPath:keyPath onObject:object nilValue:nil];
}
setKeyPath: onObject:就是調(diào)用setKeyPath: onObject: nilValue:方法禀晓,只不過nilValue傳遞的是nil精续。
最后
關(guān)于RACSignal的所有操作底層分析實(shí)現(xiàn)都已經(jīng)分析完成。最后請(qǐng)大家多多指教匆绣。