9.15更新
補(bǔ)充關(guān)于NSURLConnection
的category方法+rac_sendAsynchronousRequest
源碼解析
補(bǔ)充關(guān)于rac_signalForSelector
的源碼解析
9.14更新
補(bǔ)充了關(guān)于block回調(diào)的內(nèi)容
補(bǔ)充了關(guān)于delegate回調(diào)的內(nèi)容
例行廢話
原本打算寫剩下的一些集合類的方法的, 后面發(fā)現(xiàn)里面的東西基本上都差不多, 如果理解了整個(gè)NSArray在ReactiveCocoa下的本質(zhì)工作, 那么剩余的都可以類推, 或者本身就是掛靠在NSArray上的, 例如: NSDictionary的3個(gè)拓展方法都和NSArray有關(guān). 這個(gè)看看頭文件, 寫幾行測試代碼即可.
這一回主要看看輔助方法, 也就是一些在實(shí)際代碼編寫中能夠初步用到的, 當(dāng)然, NSArray和其它集合類是平時(shí)有需要的話也是可以用到的, 只是大多數(shù)人可能不愿意為了這么點(diǎn)特性來引入一個(gè)這么復(fù)雜的框架.
異步機(jī)制
記得在RAC的github里, README有這么介紹RAC的一段話:
One of the major advantages of RAC is that it provides a single, unified approach to dealing with asynchronous behaviors, including delegate methods, callback blocks, target-action mechanisms, notifications, and KVO.
上面說RAC的一個(gè)優(yōu)勢就是提供了一個(gè)單一的機(jī)制(就是信號處理)來聯(lián)合目前所有處理異步行為, 包括代理,回調(diào)block,target-action機(jī)制,通知和KVO.因?yàn)楦鞣N回調(diào)模式使用上并沒有說一個(gè)完全統(tǒng)一的規(guī)范, 沒有人能夠確定地說在某種情況下使用A模式就一定比B模式好, 因此我們?nèi)绻陧?xiàng)目中使用RAC, 不放從這里入手, 慢慢把新增代碼改用RAC的形式來寫.
下面的篇幅我打算用一個(gè)模式一個(gè)小節(jié)來看.
Notification
先看看頭文件, 只有一個(gè)方法:
// NSNotificationCenter+RACSupport.h
- (RACSignal *)rac_addObserverForName:(NSString *)notificationName object:(id)object;
很容易看出來就是為指定的通知名和攜帶的object創(chuàng)建一個(gè)信號, 我們先用一下看看實(shí)際情況如何:
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"NotificationRAC" object:nil] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationRAC" object:@"RAC" userInfo:@{@"A":@1}];
});
// 打印出:
NSConcreteNotification 0x7f9b5ab0a720 {name = NotificationRAC; object = RAC; userInfo = {
A = 1;
}}
從打印的信息來看, 和我們自己寫一個(gè)target一個(gè)selector的傳統(tǒng)形式并沒有太大差別, 所以, 我們?nèi)タ纯磳?shí)現(xiàn):
// NSNotificationCenter+RACSupport.m
- (RACSignal *)rac_addObserverForName:(NSString *)notificationName object:(id)object {
@unsafeify(object);
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
@strongify(object);
id observer = [self addObserverForName:notificationName object:object queue:nil usingBlock:^(NSNotification *note) {
[subscriber sendNext:note];
}];
return [RACDisposable disposableWithBlock:^{
[self removeObserver:observer];
}];
}] setNameWithFormat:@"-rac_addObserverForName: %@ object: <%@: %p>", notificationName, [object class], object];
}
中間2個(gè)宏可以在代碼查看預(yù)處理之后的代碼:
@unsafeify(object) ==> @autoreleasepool {} __attribute__((objc_ownership(none))) __typeof__(object) object_weak_ = (object);;
@strongify(object) ==> @autoreleasepool {} __attribute__((objc_ownership(strong))) __typeof__(object) object = object_weak_;
本質(zhì)上就是__unsafe_unretained和__strong的宏定義, 除此之外, 核心代碼就是中間的:
[self addObserverForName:notificationName object:object queue:nil usingBlock:^(NSNotification *note) {
...
}];
所以, RAC只是借用了蘋果提供的API進(jìn)行了封裝, 只是RAC幫我們管理起來了這個(gè)通知的生命周期, 不需要我們手動(dòng)去remove掉了.
KVO
KVO是出名難用的一個(gè)模式, 但是有些情況確不得不用, 另外給個(gè)小tip, 據(jù)我個(gè)人的實(shí)際使用經(jīng)驗(yàn), 觀察者與被觀察者任意一個(gè)被析構(gòu)掉, KVO如果還未解除都會(huì)發(fā)生crash, 這種情況會(huì)在memoryWarning的時(shí)候發(fā)生, 所以用到的地方多測試一下, 另外, KVOController是解決這個(gè)問題的好幫手, Facebook出品.
回歸正題, 我們來看看在RAC里面, KVO是什么樣子:
- (RACDisposable *)rac_observeKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)observer block:(void (^)(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent))block;
直接用Block來處理了, 并且返回了一個(gè)Disposable, 意味著我們能隨時(shí)干掉這個(gè)監(jiān)聽. 為了輔助完成KVO, 新建一個(gè)測試類, 里面含有一個(gè)name的NSString屬性:
DRCallbackTest *test = [DRCallbackTest new];
RACDisposable *dis = [test rac_observeKeyPath:@"name" options:NSKeyValueObservingOptionNew observer:test block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
NSLog(@"change:%@", change);
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
test.name = @"ABC";
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[dis dispose];
test.name = @"EFG";
});
// 打印出:
change:{
kind = 1;
new = ABC;
}
如上面所看到的, 我們只能收到第一次對name的修改, 后面因?yàn)閐ispose了, 所以我們不能再繼續(xù)收到后續(xù)的KVO了. 另外observer參數(shù)傳nil也是可以的.
我們來看看實(shí)現(xiàn)吧, 代碼有點(diǎn)長, 且分了好幾個(gè)類來實(shí)現(xiàn)的, 我們就按步驟來看吧:
第一步: addObserver
我們在執(zhí)行了上面的那一段代碼之后, 內(nèi)部實(shí)現(xiàn)會(huì)創(chuàng)建RACKVOTrampoline這個(gè)類的示例來實(shí)際處理我們的監(jiān)聽, 而這個(gè)類又會(huì)把監(jiān)聽關(guān)系放在RACKVOProxy進(jìn)行管理, 先來看看proxy的addObserver方法:
// RACKVOProxy.m
- (void)addObserver:(__weak NSObject *)observer forContext:(void *)context {
NSValue *valueContext = [NSValue valueWithPointer:context];
dispatch_sync(self.queue, ^{
[self.trampolines setObject:observer forKey:valueContext];
});
}
比較簡單, self.trampolines是一個(gè)NSMapTable, 之所以用NSMapTable而不是Map, 主要是因?yàn)樗鼘ey和value是弱引用. add和remove都是同步操作的. 所謂的context其實(shí)就是一個(gè)RACKVOTrampoline的實(shí)例, 所以在proxy這里, 一個(gè)實(shí)例對應(yīng)一個(gè)監(jiān)聽. 我們我們基本可以認(rèn)定, proxy會(huì)有:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
這個(gè)惡心的方法. 所以在RACKVOTrampoline中, 有這么一行:
[strongTarget addObserver:RACKVOProxy.sharedProxy forKeyPath:self.keyPath options:options context:(__bridge void *)self];
第二步:獲得監(jiān)聽
所以, 當(dāng)name的值有變的時(shí)候, proxy會(huì)首先監(jiān)聽到:
// RACKVOProxy.m
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
NSValue *valueContext = [NSValue valueWithPointer:context];
__block NSObject *trueObserver;
dispatch_sync(self.queue, ^{
trueObserver = [self.trampolines objectForKey:valueContext];
});
if (trueObserver != nil) {
[trueObserver observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
然后調(diào)用RACKVOTrampoline的監(jiān)聽實(shí)現(xiàn):
// RACKVOTrampoline.m
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context != (__bridge void *)self) {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
return;
}
RACKVOBlock block;
id observer;
id target;
@synchronized (self) {
block = self.block;
observer = self.observer;
target = self.weakTarget;
}
if (block == nil || target == nil) return;
block(target, observer, change);
}
所以, 從始至終就沒有我們之前傳入那個(gè)observer什么事, 因此它為空也是可以收到的.
最后一步就是remove了, 里面涉及到很多生命周期的管理, 比較復(fù)雜, 一句話粗略概括的話, 就是如果傳入的監(jiān)聽者和target被dealloc掉了, 那就要調(diào)用相對應(yīng)的dispose, 這個(gè)做法是貫穿了整個(gè)RAC框架的, 因?yàn)楸容^復(fù)雜, 所以要獨(dú)立成章節(jié)來看, 在這個(gè)主題下先不細(xì)看了, 以免丟失主題.
另外, 因?yàn)镵VO在響應(yīng)式里面占了重頭戲, 所以RAC針對這塊也有相應(yīng)的宏來簡寫這塊實(shí)現(xiàn):
[RACObserve(test, name) subscribeNext:^(id x) {
NSLog(@"name = %@", x);
}];
上面的測試代碼換成這樣也是可以的.
target-action
這個(gè)異步機(jī)制在目前來說用的最多的應(yīng)該就是UIButton加action的地方了吧, 因?yàn)檫@個(gè)方法繼承自UIControl, 所以我們看看對UIControl的拓展:
// UIControl+RACSignalSupport.h
- (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents;
只有一個(gè)方法, 有前面的經(jīng)驗(yàn)我們基本上就知道怎么使用了:
... // new一個(gè)button, 加在self.view上
[[self.btn
rac_signalForControlEvents:UIControlEventTouchUpInside]
subscribeNext:^(id x) {
NSLog(@"clicked %@", x);
}];
// 每次點(diǎn)擊打印出:
clicked <UIButton: 0x7fc15a016270; frame = (100 100; 100 100); opaque = NO; layer = <CALayer: 0x7fc15a00d110>>
內(nèi)部實(shí)現(xiàn)則是直接創(chuàng)建一個(gè)信號, 以subcriber為target, 然后設(shè)置selector為sendNext, 所以每次btn被點(diǎn)擊都會(huì)調(diào)用[subcriber sendNext:]:
- (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents {
@weakify(self);
return [[RACSignal
createSignal:^(id<RACSubscriber> subscriber) {
@strongify(self);
[self addTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
[self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
[subscriber sendCompleted];
}]];
return [RACDisposable disposableWithBlock:^{
@strongify(self);
[self removeTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
}];
}]
setNameWithFormat:@"%@ -rac_signalForControlEvents: %lx", self.rac_description, (unsigned long)controlEvents];
}
代碼實(shí)現(xiàn)比較簡單, 只是里面涉及到一些生命周期管理的東西, 差不多就是在Button要被dealloc的時(shí)候執(zhí)行一次sendCompleted:, sendCompleted會(huì)調(diào)用disposable的dispose, 所以會(huì)removeTarget:action:forControlEvents:
// RACSubscriber.m
- (void)sendCompleted {
@synchronized (self) {
void (^completedBlock)(void) = [self.completed copy];
[self.disposable dispose];
if (completedBlock == nil) return;
completedBlock();
}
}
block
從嚴(yán)格的角度上來說, block并沒有被替換掉, 畢竟Signal subscript的回調(diào)也還是block, 不過從概念上來說block回調(diào)和Signal subscript還是有本質(zhì)區(qū)別的.
既然官方文檔已經(jīng)提了, 那就講一個(gè)經(jīng)典常用的例子吧:
在NSURLConnection中有一個(gè)
+ (void)sendAsynchronousRequest:(NSURLRequest*) request queue:(NSOperationQueue*) queue completionHandler:(void (^)(NSURLResponse* __nullable response, NSData* __nullable data, NSError* __nullable connectionError)) handler
方法, 通過block來回調(diào)請求的response或者error, 在RAC的世界中, NSURLConnection也被拓展了:
+ (RACSignal *)rac_sendAsynchronousRequest:(NSURLRequest *)request
接收一個(gè)request, 返回一個(gè)Signal, 剩下的事情就是對這個(gè)信號的操作了, map, reduceEach等等都可以網(wǎng)上加了, 這里給出一個(gè)我在demo中寫的下載圖片完整例子:
- (RACSignal *)fetchInfos
{
@weakify(self);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://ryan.com/getList"]];
return [[[[[[NSURLConnection rac_sendAsynchronousRequest:request]
reduceEach:^id(NSURLResponse *response, NSData *data){
return data;
}]
deliverOn:[RACScheduler mainThreadScheduler]]
map:^id(NSData*data){
id results =
[NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
return [[[results[@"list"] rac_sequence]
map:^id(NSDictionary *info){
@strongify(self);
DRModel *model = [[DRModel alloc] initWithDictionary:info];
[self downloadImageForModel:model];
return model;
}]
array];
}]
publish]
autoconnect];
}
那個(gè)URL是我隨便構(gòu)造的, 然后mock了HTTP請求, 返回我自己要的數(shù)據(jù).
reduceEach會(huì)遍歷所有的元素, 然后替換掉原本的, 本質(zhì)上也是調(diào)用的map, 只是map只接收一個(gè)參數(shù), 所以response和data會(huì)被包裝為RACTuple, 要一個(gè)個(gè)取出來, 用reduce可以接收多個(gè)參數(shù), 所以用reduceEach更加方便和清晰. 對reduceEach源碼感興趣的同學(xué)最后附錄會(huì)有解析. 在reduceEach里我們?nèi)绻麤]有額外的需求直接返回我們感興趣的data即可.
deliverOne到了mainThreadScheduler是因?yàn)榻酉聛砜赡芤秩綰I了, 要切換回主線程;
我構(gòu)造的數(shù)據(jù)中, data反序列化之后其實(shí)是一個(gè)map, 里面有一個(gè)list, list里面又是map, 裝著我要初始化Model的信息.
至于publish和autoconnect則是和信號有關(guān), 這里只提一下前者是把Signal變?yōu)閙ulticastConnection用的, 后者是在有人subscribe的情況下才進(jìn)行連接, 也就是激活signal.
這里就完整地把NSURLConnection的completionBlock給替換掉了. 雖然看起來做了更多的活, 但實(shí)際上我們把所有相關(guān)的代碼都集中處理了, 不需要各個(gè)方法, block跟來跟去看執(zhí)行情況.
源碼部分今天暫時(shí)不分析了, 后續(xù)更新在附錄上.
Delegate
delegate其實(shí)個(gè)人感覺比較蛋疼, 為了少寫一個(gè)方法, 然后去弄這么個(gè)東西出來, 個(gè)人感覺不是很好用, 我覺得RAC的作者自己也覺得用的人不多, 所以默認(rèn)都不包含這個(gè)東西的頭文件. 而且最主要的是, 它只能替換掉返回void的方法, 作用十分有限, 我們以替換tableViewDelegate中的tableView:didSelectRowAtIndexPath:為例:
要先使用Signal來替換delegate先要
#import "RACDelegateProxy.h"
, 它不包含在默認(rèn)的ReactiveCocoa.h
里.在
viewDidLoad
里寫上:
[[self rac_signalForSelector:@selector(tableView:didSelectRowAtIndexPath:)
fromProtocol:@protocol(UITableViewDelegate)]
subscribeNext:^(RACTuple *x) {
@strongify(self);
UITableView *tableView = x.first;
NSIndexPath *indexPath = x.second;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
DRPageViewController *pageVC = [[DRPageViewController alloc] initWithPhotoModels:self.datas currentPhotoIndex:indexPath.row];
pageVC.delegate = self;
[self.navigationController pushViewController:pageVC animated:YES];
}];
- 最后別忘了
self.tableView.delegate = self;
源碼也比較簡單, 下次更新再補(bǔ)上分析吧.
結(jié)語
這章涉及到了Signal, 但是因?yàn)閟ignal的話題太大, 必須新開一章節(jié)來學(xué)習(xí), 所以就先不講了.
附錄
reduceEach
reduceEach是RACStream的方法:
// RACStream.m
- (instancetype)reduceEach:(id (^)())reduceBlock {
NSCParameterAssert(reduceBlock != nil);
__weak RACStream *stream __attribute__((unused)) = self;
return [[self map:^(RACTuple *t) {
NSCAssert([t isKindOfClass:RACTuple.class], @"Value from stream %@ is not a tuple: %@", stream, t);
return [RACBlockTrampoline invokeBlock:reduceBlock withArguments:t];
}] setNameWithFormat:@"[%@] -reduceEach:", self.name];
}
map中返回RACBlockTrampoline invoke了block, tuple做為參數(shù), 再接著看里面實(shí)現(xiàn):
+ (id)invokeBlock:(id)block withArguments:(RACTuple *)arguments {
NSCParameterAssert(block != NULL);
RACBlockTrampoline *trampoline = [[self alloc] initWithBlock:block];
return [trampoline invokeWithArguments:arguments];
}
只是返回一個(gè)實(shí)例, 繼續(xù)看:
- (id)invokeWithArguments:(RACTuple *)arguments {
SEL selector = [self selectorForArgumentCount:arguments.count];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]];
invocation.selector = selector;
invocation.target = self;
for (NSUInteger i = 0; i < arguments.count; i++) {
id arg = arguments[i];
NSInteger argIndex = (NSInteger)(i + 2);
[invocation setArgument:&arg atIndex:argIndex];
}
[invocation invoke];
__unsafe_unretained id returnVal;
[invocation getReturnValue:&returnVal];
return returnVal;
}
主線很明顯, 就是創(chuàng)建了一個(gè)NSInvocation對象, 然后塞入?yún)?shù), invoke, 注意這里invoke的target是自己, 返回的selector也是自己的, 往下一看就是一大堆1-15個(gè)參數(shù)的方法, 在這些方法中調(diào)用block, 傳遞參數(shù), 所以, 我們也知道了reduceEach最多支持15個(gè)參數(shù).
看一個(gè)3個(gè)參數(shù)的情況(參數(shù)沒有l(wèi)abel, 因?yàn)楫吘共皇墙o人看的)
- (SEL)selectorForArgumentCount:(NSUInteger)count {
switch (count) {
case 0: return NULL;
case 1: return @selector(performWith:);
case 2: return @selector(performWith::);
case 3: return @selector(performWith:::);
.....
}
- (id)performWith:(id)obj1 :(id)obj2 :(id)obj3 {
id (^block)(id, id, id) = self.block;
return block(obj1, obj2, obj3);
}
rac_sendAsynchronousRequest
源碼很簡單, 主要圍繞這個(gè)話題拓展開來, 說一下和網(wǎng)絡(luò)請求相關(guān)的注意事項(xiàng), 牽涉到Signal的內(nèi)容:
+ (RACSignal *)rac_sendAsynchronousRequest:(NSURLRequest *)request {
NSCParameterAssert(request != nil);
return [[RACSignal
createSignal:^(id<RACSubscriber> subscriber) {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"org.reactivecocoa.ReactiveCocoa.NSURLConnectionRACSupport";
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (response == nil || data == nil) {
[subscriber sendError:error];
} else {
[subscriber sendNext:RACTuplePack(response, data)];
[subscriber sendCompleted];
}
}];
return [RACDisposable disposableWithBlock:^{
queue.suspended = YES;
[queue cancelAllOperations];
}];
}]
setNameWithFormat:@"+rac_sendAsynchronousRequest: %@", request];
}
很清晰的主線, 創(chuàng)建一個(gè)Signal, 里面在新建隊(duì)列中發(fā)請求, 請求完成回調(diào)中根據(jù)data或error來sendNext+sendCompleted 或者 sendError.
RACSignal創(chuàng)建出來是冷信號(關(guān)于冷熱信號區(qū)別和冷信號的特征請看第三篇框架總覽. 冷信號在每次訂閱時(shí)都會(huì)觸發(fā)一次Signal的block, 那么這就有問題, 如果有多個(gè)人訂閱這個(gè)信號, 那豈不是每次都會(huì)重新發(fā)一次請求, 這明顯不合理. 所以, 在上面的例子中, 我們會(huì)用publish來轉(zhuǎn)換, publish轉(zhuǎn)換為RACMulticastConnection之后, 會(huì)把所有的訂閱都轉(zhuǎn)向另一個(gè)目標(biāo)--RACSubject, 我們知道RACSubject是熱信號, 它的代碼只會(huì)被執(zhí)行一次, 因此RACSubject會(huì)負(fù)責(zé)訂閱最初始的NSURLConnection的Signal. 結(jié)合代碼來看的話是這樣的:
// RACSignal+Operations.m
- (RACMulticastConnection *)publish {
RACSubject *subject = [[RACSubject subject] setNameWithFormat:@"[%@] -publish", self.name];
RACMulticastConnection *connection = [self multicast:subject];
return connection;
}
- (RACMulticastConnection *)multicast:(RACSubject *)subject {
[subject setNameWithFormat:@"[%@] -multicast: %@", self.name, subject.name];
RACMulticastConnection *connection = [[RACMulticastConnection alloc] initWithSourceSignal:self subject:subject];
return connection;
}
只是創(chuàng)建了一個(gè)RACMulticastConnection對象返回而已, 注意這里publish和multicast的區(qū)別是一個(gè)publish默認(rèn)是RACSubject, multicast可以傳replaySubject, 以后再看這些區(qū)別, 繼續(xù)看:
// RACMulticastConnection.m
- (id)initWithSourceSignal:(RACSignal *)source subject:(RACSubject *)subject {
NSCParameterAssert(source != nil);
NSCParameterAssert(subject != nil);
self = [super init];
if (self == nil) return nil;
_sourceSignal = source;
_serialDisposable = [[RACSerialDisposable alloc] init];
_signal = subject;
return self;
}
#pragma mark Connecting
- (RACDisposable *)connect {
BOOL shouldConnect = OSAtomicCompareAndSwap32Barrier(0, 1, &_hasConnected);
if (shouldConnect) {
self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal];
}
return self.serialDisposable;
}
- (RACSignal *)autoconnect {
__block volatile int32_t subscriberCount = 0;
return [[RACSignal
createSignal:^(id<RACSubscriber> subscriber) {
OSAtomicIncrement32Barrier(&subscriberCount);
RACDisposable *subscriptionDisposable = [self.signal subscribe:subscriber];
RACDisposable *connectionDisposable = [self connect];
return [RACDisposable disposableWithBlock:^{
[subscriptionDisposable dispose];
if (OSAtomicDecrement32Barrier(&subscriberCount) == 0) {
[connectionDisposable dispose];
}
}];
}]
setNameWithFormat:@"[%@] -autoconnect", self.signal.name];
}
這是RACMulticastConnection所有的實(shí)現(xiàn)代碼, connect和autoconnect的區(qū)別是, 一個(gè)是立即觸發(fā)連接, 一個(gè)則是返回信號, 被訂閱后則觸發(fā)連接. 需要注意的是, 最終的connect只能執(zhí)行一次, autoconnect會(huì)維護(hù)一個(gè)計(jì)數(shù)器, 在計(jì)數(shù)器歸0時(shí)會(huì)dispose掉connection.
核心的代碼是self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal];
, 我們繼續(xù)跟進(jìn)去看(有多個(gè)subscribe:方法, 目前我們的例子需要關(guān)注的是RACDynamicSignal, 因?yàn)镹SURLConnection真正返回的是這個(gè)對象):
// RACDynamicSignal.m
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
NSCParameterAssert(subscriber != nil);
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
if (self.didSubscribe != NULL) {
RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
RACDisposable *innerDisposable = self.didSubscribe(subscriber);
[disposable addDisposable:innerDisposable];
}];
[disposable addDisposable:schedulingDisposable];
}
return disposable;
}
RACDisposable *innerDisposable = self.didSubscribe(subscriber);
這行代碼觸發(fā)了NSURLConnection的連接, 所以到這里, 我們弄清楚了RACMulticastConnection最終訂閱了NSURLConnection的Signal.
現(xiàn)在進(jìn)入下半部分, 多個(gè)subscriber到底訂閱了誰呢? 回到autoconnect的代碼中:
RACDisposable *subscriptionDisposable = [self.signal subscribe:subscriber];
結(jié)合上下文我們知道, self.signal是publish中傳進(jìn)來的RACSubject, 所以這個(gè)時(shí)候要我們?nèi)ACSubject里面看看:
// RACSubject.m
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
NSCParameterAssert(subscriber != nil);
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
NSMutableArray *subscribers = self.subscribers;
@synchronized (subscribers) {
[subscribers addObject:subscriber];
}
return [RACDisposable disposableWithBlock:^{
@synchronized (subscribers) {
NSUInteger index = [subscribers indexOfObjectWithOptions:NSEnumerationReverse passingTest:^ BOOL (id<RACSubscriber> obj, NSUInteger index, BOOL *stop) {
return obj == subscriber;
}];
if (index != NSNotFound) [subscribers removeObjectAtIndex:index];
}
}];
}
維護(hù)了一個(gè)self.subscribers, 里面存了RACPassthroughSubscriber, 其實(shí)passthroughSubscriber可以不用管, 所有的subscriber和Signal的event以及Disposables都是通過它的, 為了減少復(fù)雜度, 我們可以任務(wù)self.subscribers里面添加的就是我們的subscriber, , 然后再看看下面的一系列方法:
- (void)enumerateSubscribersUsingBlock:(void (^)(id<RACSubscriber> subscriber))block {
NSArray *subscribers;
@synchronized (self.subscribers) {
subscribers = [self.subscribers copy];
}
for (id<RACSubscriber> subscriber in subscribers) {
block(subscriber);
}
}
- (void)sendNext:(id)value {
[self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:value];
}];
}
...
所以事實(shí)就很清楚了, 每次sendXXX的時(shí)候, 都會(huì)遍歷一遍, 每個(gè)都send一次, 保證每個(gè)Subscriber(要在send之前subscribe)都能收到.
下面是我demo里的代碼:
RACSignal *signal = [self fetchInfos];
RAC(self, datas) = [[[signal doCompleted:^{
@strongify(self);
[self.tableView reloadData];
}] logError]catchTo:[RACSignal empty]];
RAC(self, data2) = [[[signal doCompleted:^{
NSLog(@"second");
}]
logError]
catchTo:[RACSignal empty]];
data2只是為了演示的確是只發(fā)了一次請求用的, 沒有特別的含義. 如果沒有這種監(jiān)聽多個(gè)網(wǎng)絡(luò)請求的情況, 可以直接RAC(...) = RACObserve(...), 省去中間的singla命名.
里面東西確實(shí)有點(diǎn)多, 因?yàn)檫€沒有對Signal進(jìn)行分析, 這里只是拋出網(wǎng)絡(luò)請求這個(gè)常用且一般用法會(huì)出錯(cuò)的情況進(jìn)行討論. 個(gè)人感覺講的還不足夠清晰, 有時(shí)間我會(huì)再整理一把, 爭取把整個(gè)流程都梳理清晰.
delegateProxy
-rac_signalForSelector:fromProtocol:
是對NSObject的拓展, 所以任何對象都可以使用, 我們先直接看看代碼:
// NSObject+RACSelectorSignal.m
- (RACSignal *)rac_signalForSelector:(SEL)selector fromProtocol:(Protocol *)protocol {
NSCParameterAssert(selector != NULL);
NSCParameterAssert(protocol != NULL);
return NSObjectRACSignalForSelector(self, selector, protocol);
}
調(diào)用了一個(gè)C函數(shù)來返回, 整個(gè)C函數(shù)有好幾十行, 直接注釋在源碼里面看好了:
static RACSignal *NSObjectRACSignalForSelector(NSObject *self, SEL selector, Protocol *protocol) {
// 為了避免重名, 所以給方法加前綴
SEL aliasSelector = RACAliasForSelector(selector);
// 保證線程安全
@synchronized (self) {
// 如果已經(jīng)建立了subject 就直接返回 讓調(diào)用者訂閱即可
RACSubject *subject = objc_getAssociatedObject(self, aliasSelector);
if (subject != nil) return subject;
// 獲取類名 里面的邏輯很復(fù)雜 replace了forwardInvocation和responseToSelector等等方法 有興趣的可以深入探究一下
Class class = RACSwizzleClass(self);
NSCAssert(class != nil, @"Could not swizzle class of %@", self);
// 新建subject 并與對象關(guān)聯(lián)
subject = [[RACSubject subject] setNameWithFormat:@"%@ -rac_signalForSelector: %s", self.rac_description, sel_getName(selector)];
objc_setAssociatedObject(self, aliasSelector, subject, OBJC_ASSOCIATION_RETAIN);
// 對象被釋放時(shí)發(fā)送completed
[self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
[subject sendCompleted];
}]];
// 獲取目標(biāo)方法
Method targetMethod = class_getInstanceMethod(class, selector);
// 如果目標(biāo)方法未實(shí)現(xiàn)
if (targetMethod == NULL) {
// 先獲取typeEncoding 后面動(dòng)態(tài)添加方法時(shí)需要
const char *typeEncoding;
if (protocol == NULL) {
typeEncoding = RACSignatureForUndefinedSelector(selector);
} else {
// 獲取一下方法的描述 也是后面新增method
struct objc_method_description methodDescription = protocol_getMethodDescription(protocol, selector, NO, YES);
if (methodDescription.name == NULL) {
methodDescription = protocol_getMethodDescription(protocol, selector, YES, YES);
NSCAssert(methodDescription.name != NULL, @"Selector %@ does not exist in <%s>", NSStringFromSelector(selector), protocol_getName(protocol));
}
typeEncoding = methodDescription.types;
}
RACCheckTypeEncoding(typeEncoding);
// 動(dòng)態(tài)添加一個(gè)方法
if (!class_addMethod(class, selector, _objc_msgForward, typeEncoding)) {
// 添加失敗是因?yàn)橐呀?jīng)有一個(gè)這樣名字的方法了
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedString(@"A race condition occurred implementing %@ on class %@", nil), NSStringFromSelector(selector), class],
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Invoke -rac_signalForSelector: again to override the implementation.", nil)
};
return [RACSignal error:[NSError errorWithDomain:RACSelectorSignalErrorDomain code:RACSelectorSignalErrorMethodSwizzlingRace userInfo:userInfo]];
}
// 目標(biāo)方法不等于runtime轉(zhuǎn)發(fā)方法
} else if (method_getImplementation(targetMethod) != _objc_msgForward) {
// 已有實(shí)現(xiàn)了 用已有的實(shí)現(xiàn)來取一個(gè)別名 事實(shí)證明 注釋掉這段代碼也沒什么問題 所以沒想明白這里addMethod的意義在哪里 求解釋~~
const char *typeEncoding = method_getTypeEncoding(targetMethod);
RACCheckTypeEncoding(typeEncoding);
BOOL addedAlias __attribute__((unused)) = class_addMethod(class, aliasSelector, method_getImplementation(targetMethod), typeEncoding);
NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), class);
// 因?yàn)橐呀?jīng)有了實(shí)現(xiàn)了, 所以用runtime轉(zhuǎn)發(fā)來替換掉原來的實(shí)現(xiàn) 這樣就會(huì)轉(zhuǎn)發(fā)到別名的方法上面去了
class_replaceMethod(class, selector, _objc_msgForward, method_getTypeEncoding(targetMethod));
}
return subject;
}
}
里面還有一些細(xì)節(jié)沒有講解, 因?yàn)槭巧婕暗絩untime的東西, 和主題不太掛鉤, 所以也就沒細(xì)講了, 感興趣的可以去看看, 還是能學(xué)到不少runtime的知識的.