RacSignal
- ReactiveCocoa
- RACSignal使用代码
- RacSingal源码分析
- RACSignal 创建信号
- RACDynamicSignal 创建信号
- subscribeNext 订阅信号
- subscribe 订阅订阅者
- sendNext 发送信号
- 实现图![RACSignal](https://img-blog.csdnimg.cn/20200414094334425.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zOTY0NzQxNQ==,size_16,color_FFFFFF,t_70)
- 思路总结:
- 分析源码下载
ReactiveCocoa
ReactiveCocoa(简称RAC)是由GitHub团队开源的一套基于Cocoa的并且具有FRP特性的框架。FRP(Functional Reactive Programming)即响应式编程。RAC就是一个第三方库,使用它可以大大简化代码,提高开发效率,目前公司也在范围使用。但疏于总结只是停留在会用的阶段,这次针对RAC做个全面认识和总结。 第一部分基础理论。 第二部分介绍一些常用类。 第三部分介绍一些常用语法。
RACSignal使用代码
//1.创建信号
RACSignal *singal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
//3.发送信号
[subscriber sendNext:@"1"];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"RACDisposable...");
}];
}];
//2.订阅信号
[singal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
使用RacSignal需要三个步骤:
1.创建信号
[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {}];
2.订阅信号
[singal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
3.发送信号
//3.发送信号
[subscriber sendNext:@"1"];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"RACDisposable...");
}];
RacSingal源码分析
RACSignal 创建信号
[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {}];
首先,进入createSignal我们可以看到内部声明的类方法用来创建一个RACSignal类。
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
return [RACDynamicSignal createSignal:didSubscribe];
}
观察Block块,我们不难发现需要Block返回值为RACDisposable,参数为subscriber订阅者。
在利用createSignal创建的信号开始为冷信号,同时在block块中,我们需要返回值
return nil;
或者
return [RACDisposable disposableWithBlock:^{
NSLog(@"RACDisposable...");
}];
来生成RACDisposable。
我们看到createSignal中并没有直接进行创建操作,而是调用了RACDynamicSignal这个类进行创建信号。
我们继续深入看RACDynamicSignal 如何生成冷信号。
RACDynamicSignal 创建信号
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
RACDynamicSignal *signal = [[self alloc] init];
signal->_didSubscribe = [didSubscribe copy];
return [signal setNameWithFormat:@"+createSignal:"];
}
1.先进行了alloc init来创建RACDynamicSignal的对象signal;
2.通过将传入的didSubscribe拷贝给signal自己的didSubscribe;
3.返回当前对象 – 为当前对象设置一个名称标签
观察init方法
- (instancetype)init {
self = [super init];
if (self == nil) return nil;
// As soon as we're created we're already trying to be released. Such is life.
[self invalidateGlobalRefIfNoNewSubscribersShowUp];
return self;
}
- (void)invalidateGlobalRefIfNoNewSubscribersShowUp {
// If no one subscribes in one pass of the main run loop, then we're free to
// go. It's up to the caller to keep us alive if they still want us.
RACSignalList *elem = malloc(sizeof(*elem));
// This also serves to retain the signal until the next pass.
elem->retainedSignal = CFBridgingRetain(self);
OSAtomicEnqueue(&RACActiveSignalsToCheck, elem, offsetof(RACSignalList, next));
// Not using a barrier because duplicate scheduling isn't erroneous, just
// less optimized.
int32_t willCheck = OSAtomicOr32Orig(1, &RACWillCheckActiveSignals);
// Only schedule a check if RACWillCheckActiveSignals was 0 before.
if (willCheck == 0) {
dispatch_async(dispatch_get_main_queue(), ^{
RACCheckActiveSignals();
});
}
}
1.RACSignalList – 结构体
typedef struct RACSignalList {
CFTypeRef retainedSignal;
struct RACSignalList * restrict next;
} RACSignalList;
官方给出的解释是
A linked list of RACDynamicSignals, used in RACActiveSignalsToCheck.
(RACActiveSignalsToCheck中使用的RACDynamicSignals的链接列表)
通过将self(也就是自身的RACDynamicSignal)转换成为CF类型(在源码中多次用到了CFBridgingRetain()函数),赋值给elem结构体对象的保留信号。
实现自动入队
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_4_0)
void OSAtomicEnqueue( OSQueueHead *__list, void *__new, size_t __offset);
观察传入的参数分别是
static OSQueueHead RACActiveSignalsToCheck = OS_ATOMIC_QUEUE_INIT;
elem
offsetof(RACSignalList, next)
思考:为什么设计成为静态???
个人认为,设计成为静态是为了在程序运行时候,会多次创建信号,创建信号就会调用到init方法,从而设计成为static变成静态是为了只加载一次内存,从而防止出现内存重复加载问题。
这个方法,核心做的事情是将信号保存到一个保留信号列表中,为后期响应式编程提供了有力的条件。
subscribeNext 订阅信号
一旦订阅了信号,此信号就从冷信号变成了热信号。
思考:我们是先发送还是先订阅???
个人认为,在RACSignal中,我们应该先订阅后发送,这是因为,如果我们先发送,那么订阅就变的毫无意义,但是,总有特别的案例,后期,我们会单独讲解ReactiveCocoa提供的先订阅后发送的类。
订阅信号,通过subscribeNext来实现
[singal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
同样,我们进入内部来观察它具体做了什么操作
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL);
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o];
}
我们来看核心部分,还是一样,它并没有直接进行订阅,而是调用了RACSubscriber来订阅信号,传入参数,我们把需要订阅的信号传入。
#import "RACSubscriber.h"
/// A simple block-based subscriber.
@interface RACSubscriber : NSObject <RACSubscriber>
/// Creates a new subscriber with the given blocks.
+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed;
源码提供这个订阅者类十分简介,只有一个对外的类方法,用于创建对象。
我们看到,这个类继承自NSObject,其约束于RACSubscriber
subscriber->_next = [next copy];
subscriber->_error = [error copy];
subscriber->_completed = [completed copy];
内部操作,也十分惹人喜欢,只是将对应参数进行了保存。
做完这些操作后,RACSingal订阅这个订阅者即可。
return [self subscribe:o];
千万不要被迷惑,订阅者传入的block参数id x此时作为next已经被订阅者所管理。等待的就是被调用。
subscribe 订阅订阅者
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
NSCParameterAssert(subscriber != nil);
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
OSSpinLockLock(&_subscribersLock);
if (_subscribers == nil) {
_subscribers = [NSMutableArray arrayWithObject:subscriber];
} else {
[_subscribers addObject:subscriber];
}
OSSpinLockUnlock(&_subscribersLock);
@weakify(self);
RACDisposable *defaultDisposable = [RACDisposable disposableWithBlock:^{
@strongify(self);
if (self == nil) return;
BOOL stillHasSubscribers = YES;
OSSpinLockLock(&_subscribersLock);
{
// 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];
stillHasSubscribers = _subscribers.count > 0;
}
}
OSSpinLockUnlock(&_subscribersLock);
if (!stillHasSubscribers) {
[self invalidateGlobalRefIfNoNewSubscribersShowUp];
}
}];
[disposable addDisposable:defaultDisposable];
if (self.didSubscribe != NULL) {
RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
RACDisposable *innerDisposable = self.didSubscribe(subscriber);
[disposable addDisposable:innerDisposable];
}];
[disposable addDisposable:schedulingDisposable];
}
return disposable;
}
这个函数看起来很可怕,不过这是最核心的部分,订阅订阅者。我们静下心来分析核心部分。
sendNext 发送信号
发送信号,是通过RACSubsriber来发送的。这个就验证了之前先订阅(通过RACSubsriber,从而保存订阅的next),在发送
- (void)sendNext:(id)value {
/* 发送信号可能会导致出现多个发送信号同时发送,出现交叉的情况。因此,需要使用同步锁 */
@synchronized (self) {
void (^nextBlock)(id) = [self.next copy];
if (nextBlock == nil) return;
nextBlock(value);
}
}
发送信号,传入的参数是id类型。
思考:为什么使用同步锁机制???
个人认为,发送信号可能会导致出现多个发送信号同时发送,出现交叉的情况。因此,需要使用同步锁。
观察核心代码
源码先声明了一个名称为nextBlock的Block块用于存储自身next的值,这个值在之前有保存,nextBlock为空,则什么都不做,直接返回。否则记录当前值。
调用sendCompleted实现发送完成操作
[subscriber sendCompleted];
实现图
思路总结:
使用RACSignal实际上内部做了这几个操作
- 利用RACDynamicSignal创建冷信号;
- 通过RACSubscriber订阅信号,并将定于的信号保存在RACSubscriber内部的next中;
- 订阅信号订阅订阅者subscriber;
- 通过RACSubscriber发送信号;
分析源码下载
小编已经将阅读批注的源码放到了Github上,需要的小伙伴可以下载阅读,如果有哪里不正确或者有什么问题,可以一起评论留言分享自己的学习成果。
链接: Github ReativeCocod.