RactiveObjc
是一套OC语言的实现响应式编程的一套框架, 我们通过这一套框架,我们项目中如果使用MVVM架构,用这个框架更方便得实现VM与V/C之间的绑定。
// 假设VM里面是使用RACSignal *OM021Signal,C控制器中通过subscribeNext:订阅VM的OM021Signal来接收回调消息
// VM中的信号
- (RACSignal *)OM021Signal {
if (!_OM021Signal) {
@weakify(self);
_OM021Signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self);
NSMutableDictionary *infoDict = [NSMutableDictionary dictionary];
[[YSTNetworkingManager new] sendHttp:OM021 content:infoDict controller:self.vc animate:NO completion:^(NSString *msgCode, NSString *isMore, NSDictionary *backCodeDict, NSDictionary *backContentDict, NSArray *backContentListDict, NSDictionary *backHeadDict) {
@strongify(self);
NSNumber *nomoreDataStatu = [busiState isEqualToString:@"00"] ? @(0) : @(1);
[subscriber sendNext:nomoreDataStatu];
[subscriber sendCompleted];
} error:^(NSError *connectionError) {
[subscriber sendNext:@(2)];
[subscriber sendCompleted];
}];
return [RACDisposable disposableWithBlock:^{ }];
}];
}
return _OM021Signal;
}
一、RACSignal的实现原理
1. 创建信号RACSignal
首先调用[RACDynamicSignal createSignal:didSubscribe];
创建RACDynamicSignal记录(设置属性值)didSubscribe
(是一个返回RACDisposable * 的block)。
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
RACDynamicSignal *signal = [[self alloc] init];
signal->_didSubscribe = [didSubscribe copy];
return [signal setNameWithFormat:@"+createSignal:"];
}
// 比如实际使用时控制器中订阅信号的sendNext:
@weakify(self);
[self.om021ViewModel.OM021Signal subscribeNext:^(NSNumber *nomoreDataStatu) {
@strongify(self);
if ([nomoreDataStatu integerValue] == 0) {
[self.mainView reloadWithOM021Model:self.om021ViewModel];
}
}];
2. 信号的订阅
对信号我们可以有3种订阅:
- (RACDisposable *)subscribeNext:(void (^)(ValueType _Nullable x))nextBlock;
- (RACDisposable *)subscribeError:(void (^)(NSError * _Nullable error))errorBlock;
- (RACDisposable *)subscribeCompleted:(void (^)(void))completedBlock;
2.1 由于3种订阅的原理都是相通的,所以我们分析其中一种就可以了。我们对上面信号subscribeNext:
订阅时, nextBlock最终收到回调的过程是这样的:
- 创建
RACSubscriber
,引用nextBlock. - 创建
RACPassthroughSubscriber
,引用上一步中的RACSubscriber、self(即RACDynamicSignal)、disposable(创建了RACCompoundDisposable,负责清除订阅信息的) - 调用
didSubscribe(subscriber)
(在创建RACSignal时记录的那个block),外部使用时在didSubscribe
中可以拿到订阅者subscriber
。 - 使用
[subscriber sendNext:@1];
之后,会拿到对应的nextBlock调用。 -
subscribeNext:
的nextBlock收到回调。
// 1. 创建RACSubscriber,引用nextBlock
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o]; // self == RACDynamicSignal
}
// 2.创建RACPassthroughSubscriber,
// 引用上一步中的RACSubscriber、self(即RACDynamicSignal)、disposable.
// 最后执行订阅的self.didSubscribe(subscriber)
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
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;
}
2.2 订阅者subscriber
可以发送3种信号
// 可以自己根据实际情况去发送对应的信号
// subscriber调用的sendNext方法实现:
- (void)sendNext:(id)value {
void (^nextBlock)(id) = [self.next copy];
if (nextBlock == nil) return;
nextBlock(value);
}
- (void)sendError:(NSError *)e {
void (^errorBlock)(NSError *) = [self.error copy];
[self.disposable dispose];
if (errorBlock == nil) return;
errorBlock(e);
}
- (void)sendCompleted {
void (^completedBlock)(void) = [self.completed copy];
[self.disposable dispose];
if (completedBlock == nil) return;
completedBlock();
}
3. 信号订阅的销毁
信号有订阅时会产生的各个对象之间的引用.png我们关心订阅时要收到回调也当然要关心如何解除订阅,订阅相关的nextBlock等的销毁会随着订阅者subscriber的销毁发生. 我们一般会让ViewModel持有信号的,信号销毁跟随ViewModel的。
在信号收到订阅时,通过源码可知,subscriber
引用了RACCompoundDisposable
,这个对象会引用相关的RACDisposable
。首先订阅者subscriber
是一个临时的对象,在超出作用域后就会销毁。
subscriber在销毁时,它的属性:RACDisposable
、订阅信号的nextBlock、completedBlock、errorBlock也跟着销毁。
- (void)dealloc {
// 我们创建信号的时候可以将需要随着订阅结束而移除的对象放在dispose当中
// 创建信号时候的返回值return [RACDisposable disposableWithBlock:^{ // 需销毁的代码}];
[self.disposable dispose];
}
如果是subscriber
有被强引用了的话,比如如下情况,可以手动调用:
/** 在RACDisposable中有self.subscriber = nil;来完成订阅者及时的释放。
下面3种方式都可以触发:
1. [subscriber sendCompleted];
2. [subscriber sendError:error];
3. 创建信号时在didSubscribe返回的RACDisposable对象, 手动调用disposable方法。
*/
@weakify(self);
_scoreRankSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
@strongify(self);
self.subscriber = subscriber;
return [RACDisposable disposableWithBlock:^{
@strongify(self);
self.subscriber = nil;// 不需要用了进行清理
}];
}];
二、RACSubject的使用
RACSubject
是继承于RACSignal
的一个信号,相比其父类RACSignal
,它增加的功能是实现了<RACSubscriber>
协议,自己即是信号也是订阅者,可以发信号给自己的订阅者们。
它内部用数组记录了所有订阅者,在每次外部订阅自己时,也是像RACSignal的订阅一样,创建RACPassthroughSubscriber
, 然后添加在自己的subscribers
数组中.
发送信号时调用- (void)sendNext:(id)value
, 遍历数组逐个调用[subscriber sendNext:value];。
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
NSMutableArray *subscribers = self.subscribers;
@synchronized (subscribers) {
[subscribers addObject:subscriber];
}
// 这个disposable中的block是在订阅结束之后会调用,起到的作用是从数组中移除一个订阅者
// 订阅结束的方式:sendCompleted、sendError、调用dispose方法
[disposable addDisposable:[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];
}
}]];
return disposable;
}
// RACSubject对象可以主动调用sendNext:方法,
// 内部实现是遍历自己的订阅者数组subscribers, 逐个调用[subscriber sendNext:value];
- (void)sendNext:(id)value {
[self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
[subscriber sendNext:value];
}];
}
什么时候销毁呢?
在使用时我们首先是创建一个RACSubject
对象,然后可以对这个进行订阅会将订阅者加入数组中,当RACSubject
对象销毁后,数组中每个订阅者也会销毁
三、RACReplaySubject
继承于RACSubject
的类,在父类的基础上增加了对发送过的数据保存到数组的功能,在有订阅时,会先把之前保存的数据先sendNext
一遍。
四、RAC的一些使用注意
RAC的源码追踪,可以发现,如果在block中引用了self
,需要使用@weakify(self),否则会导致内存泄漏。
正确使用@weakify 和@strongify防止block循环引用
网友评论