zoukankan      html  css  js  c++  java
  • ReactiveCocoa源码解读(一)

    本着饮水思源的想法,面对ReactiveCocoa的强大功能,按捺不住心中的好奇心,于是走进其源码之中,一探ReactiveCocoa的魅力所在。虽然,耳闻其强大功能的核心是:信号,但一直不知道这个信号是如何产生、如何传递,又是如何被处理的。曾经以为信号传递是通知,但是真正读了源码后,才发现之前的想法有多不妥,而人家的实现又是多巧妙。

    本文主要从ReactiveCocoa的主要类入手,通过剖析其整个应用过程中,信号的生命周期来领略其编程之美。

    一、RACSignal

    1. 应用

    // 1.创建信号
    RACSignal *siganl = [RACSignal createSignal:^RACDisposable *(id subscriber) {
    	// 注:block在此仅仅是个参数,未被调用,
    	//当有订阅者订阅信号时会调用block。
    
    	// 2.发送信号
    	[subscriber sendNext:@1];
    	// 如果不在发送数据,最好发送信号完成,内部会自动调用[RACDisposable disposable]取消订阅信号。
    	return nil;
    }];
    
    // 3.订阅信号,才会激活信号.
    [siganl subscribeNext:^(id x) {
    	// block调用时刻:每当有信号发出数据,就会调用block.
    	NSLog(@"接收到数据:%@",x);
    }];
    

    2.源码实现

    • 创建信号

    +(RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe;

    // RACDynamicSignal.m
    
    + (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe {
    	//创建了一个RACDynamicSignal类的信号
    	RACDynamicSignal *signal = [[self alloc] init];
    	//将代码块保存到信号里面(但此时仅仅是保存,没有调用),所以信号还是冷信号
    	signal->_didSubscribe = [didSubscribe copy];
    	return [signal setNameWithFormat:@"+createSignal:"];
    }
    
    • 订阅信号
    (RACDisposable *)subscribeNext:(void (^ )(id x))nextBlock;
    
    // RACSignal.m
    - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    	NSCParameterAssert(nextBlock != NULL);
    	// 内部创建了RACSubscriber(订阅者)类的实例对象o,并且将nextBlock保存到o中,在返回值出执行o,实际也是执行了nextBlock。
    	RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    	return [self subscribe:o];
    }
    
    // RACSubscriber.m
    + (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
    	RACSubscriber *subscriber = [[self alloc] init];
    	// 将block保存到subscriber中
    	subscriber->_next = [next copy];
    	subscriber->_error = [error copy];
    	subscriber->_completed = [completed copy];
    
    	return subscriber;
    }
    
    // RACDynamicSignal.m
    - (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    	NSCParameterAssert(subscriber != nil);
    
    	RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
    	subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
    	//判断有无self.didSubscribe,有则执行该self.didSubscribe,意味着将订阅者subscriber发送过去
    	if (self.didSubscribe != NULL) {
    		RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
    			RACDisposable *innerDisposable = self.didSubscribe(subscriber);
    			[disposable addDisposable:innerDisposable];
    		}];
    
    		[disposable addDisposable:schedulingDisposable];
    	}
    	
    	return disposable;
    }
    
    // RACPassthroughSubscriber.m
    - (instancetype)initWithSubscriber:(id)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable {
    	NSCParameterAssert(subscriber != nil);
    
    	self = [super init];
    	if (self == nil) return nil;
    	// 保存订阅者,信号,处理操作
    	_innerSubscriber = subscriber;
    	_signal = signal;
    	_disposable = disposable;
    
    	[self.innerSubscriber didSubscribeWithDisposable:self.disposable];
    	return self;
    }
    
    • 发送信号
    [subscriber sendNext:@1]
    
    // RACPassthroughSubscriber.m
    - (void)sendNext:(id)value {
    	if (self.disposable.disposed) return;
    
    	if (RACSIGNAL_NEXT_ENABLED()) {
    		RACSIGNAL_NEXT(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString([value description]));
    	}
    
    	[self.innerSubscriber sendNext:value];
    }
    
    // RACSubscriber.m
    - (void)sendNext:(id)value {
    	@synchronized (self) {
    		void (^nextBlock)(id) = [self.next copy];
    		if (nextBlock == nil) return;
    		// 名为next的block是返回值为void,参数为id类型的value,在sendNext:内部,将next复制给nextBlock,执行该方法后,subscribeNext:的block参数才会被调用。
    		nextBlock(value);
    	}
    }
    

    3.流程图

    RACSignal

    4.总结

    先创建信号,然后订阅信号,最后执行didSubscribe内部的方法,顺序是不能变的

    RACSignal底层实现

    * 1.创建信号,首先把didSubscribe保存到信号中,还不会触发。
    * 2.当信号被订阅,也就是调用signal的subscribeNext:nextBlock
    	2.1 subscribeNext内部会创建订阅者subscriber,并且把nextBlock保存到subscriber中。
    	2.2 subscribeNext内部会调用siganl的didSubscribe
    * 3.siganl的didSubscribe中调用[subscriber sendNext:@1];
    	3.1 sendNext底层其实就是执行subscriber的nextBlock
    

    二、RACSubject

    1. 应用

    // 创建信号
    RACSubject *subject = [RACSubject subject];
    
    // 订阅
    [subject subscribeNext:^(id x) {
    	NSLog(@"第一个订阅者:%@", x);
    }];
    
    // 发送信号
    [subject sendNext:@"1"];
    

    2.源码实现

    • 创建信号
    // RACSubject.m
    + (instancetype)subject {
    	return [[self alloc] init];
    }
    
    - (id)init {
    	self = [super init];
    	if (self == nil) return nil;
    
    	_disposable = [RACCompoundDisposable compoundDisposable];
    	_subscribers = [[NSMutableArray alloc] initWithCapacity:1];
    
    	return self;
    }
    
    • 订阅信号

    RACSubject订阅信号的实质就是将内部创建的订阅者保存在订阅者数组self.subscribers中,仅此而已。订阅者对象有一个名为nextBlock的block参数

    // RACSignal.m
    - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    	NSCParameterAssert(nextBlock != NULL);
    
    	RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    	return [self subscribe:o];
    }
    
    // RACSubscriber.m
    + (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
    	RACSubscriber *subscriber = [[self alloc] init];
    
    	subscriber->_next = [next copy];
    	subscriber->_error = [error copy];
    	subscriber->_completed = [completed copy];
    
    	return subscriber;
    }
    
    // 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) {
    			// Since newer subscribers are generally shorter-lived, search
    			// starting from the end of the list.
    			NSUInteger index = [subscribers indexOfObjectWithOptions:NSEnumerationReverse passingTest:^ BOOL (id<RACSubscriber> obj, NSUInteger index, BOOL *stop) {
    				return obj == subscriber;
    			}];
    
    			if (index != NSNotFound) [subscribers removeObjectAtIndex:index];
    		}
    	}];
    }
    
    • 发送信号
    底层实现:
    1. 先遍历订阅者数组中的订阅者;
    2. 后执行订阅者中的nextBlock;
    3. 最后让订阅者发送信号
    
    // RACSubject.m
    - (void)sendNext:(id)value {
    	[self enumerateSubscribersUsingBlock:^(id subscriber) {
    		[subscriber sendNext:value];
    	}];
    }
    
    - (void)enumerateSubscribersUsingBlock:(void (^)(id subscriber))block {
    	NSArray *subscribers;
    	@synchronized (self.subscribers) {
    		subscribers = [self.subscribers copy];
    	}
    
    	for (id subscriber in subscribers) {
    		block(subscriber);
    	}
    }
    

    3.流程图

    RACSubject

    4.总结

    RACSubscriber:表示订阅者的意思,用于发送信号,这是一个协议,不是一个类,只要遵守这个协议,并且实现方法才能成为订阅者。通过create创建的信号,都有一个订阅者,帮助他发送数据。

    RACDisposable:用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它。

    RACSubject的底层与RACSignal不一样:

    • 调用subscribeNext订阅信号,只是把订阅者保存起来,并且订阅者的nextBlock已经赋值了
    • 调用sendNext发送信号,遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock
    • 由于本质是将订阅者保存到数组中,所以可以有多个订阅者订阅信息。

    必须先订阅,后发送信息。订阅信号就是创建订阅者的过程,如果不先订阅,数组中就没有订阅者对象,那就通过订阅者发送消息

    三、RACReplaySubject

    1.应用

    RACReplaySubject即可以先订阅后发送信号,也可以反过来

    RACReplaySubject *subject = [RACReplaySubject subject];
    
    // 第一次订阅
    [subject subscribeNext:^(id x) {
    	NSLog(@"%@", x);
    }];
    
    // 发送信号
    [subject sendNext:@"1"];
    
    // 第一次订阅
    [subject subscribeNext:^(id x) {
    	NSLog(@"%@", x);
    }];
    

    2.源码实现

    • 创建信号
    // RACSubject.m
    + (instancetype)subject {
    	return [[self alloc] init];
    }
    
    // RACReplaySubject.m
    - (instancetype)init {
    	return [self initWithCapacity:RACReplaySubjectUnlimitedCapacity];
    }
    
    - (instancetype)initWithCapacity:(NSUInteger)capacity {
    	self = [super init];
    	if (self == nil) return nil;
    
    	_capacity = capacity;
    	// 会用这个数组保存值value
    	_valuesReceived = (capacity == RACReplaySubjectUnlimitedCapacity ? [NSMutableArray array] : [NSMutableArray arrayWithCapacity:capacity]);
    
    	return self;
    }
    
    • 订阅信号
    // RACSignal.m
    - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
    	NSCParameterAssert(nextBlock != NULL);
    
    	RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
    	return [self subscribe:o];
    }
    
    // RACReplaySubject.m
    - (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)];
    			}
    
    			if (compoundDisposable.disposed) return;
    
    			if (self.hasCompleted) {
    				[subscriber sendCompleted];
    			} else if (self.hasError) {
    				[subscriber sendError:self.error];
    			} else {
    				RACDisposable *subscriptionDisposable = [super subscribe:subscriber];
    				[compoundDisposable addDisposable:subscriptionDisposable];
    			}
    		}
    	}];
    
    	[compoundDisposable addDisposable:schedulingDisposable];
    
    	return compoundDisposable;
    }
    
    • 发送信号
    // RACReplaySubject.m
    - (void)sendNext:(id)value {
    	@synchronized (self) {
    		//重点:发送信号的时候,会先将值value保存到数组中
    		[self.valuesReceived addObject:value ?: RACTupleNil.tupleNil];
    		//调用父类发送(先遍历订阅者,然后发送值value)
    		[super sendNext:value];
    
    		if (self.capacity != RACReplaySubjectUnlimitedCapacity && self.valuesReceived.count > self.capacity) {
    			[self.valuesReceived removeObjectsInRange:NSMakeRange(0, self.valuesReceived.count - self.capacity)];
    		}
    	}
    }
    

    3.原理图

    RACReplaySubject

    4.总结

    RACReplaySubjectRACSubject的子类
    由于每次发送信号时,会先保存nextBlock,然后调用父类的sendNext方法,遍历订阅者,执行信号;而每次订阅信号时,会从valuesReceived中取值,然后调用sendNext方法,遍历订阅者,执行信号。所以,订阅和发送没有先后顺序。


    未完待续 ......

  • 相关阅读:
    二叉树的前中后序遍历(非递归)
    剑指offer——圆圈中最后剩下的数字
    剑指offer——和为s的连续正整数序列
    leetcode300.最长上升子序列
    将网络描述符设置为非阻塞的场景
    leetcode72.编辑距离
    浅谈各种锁机制
    TCP如何保证可靠传输?
    JavaScript(七)
    JavaScript(六)
  • 原文地址:https://www.cnblogs.com/fishbay/p/7206663.html
Copyright © 2011-2022 走看看