简介
相信不用过多介绍都知道 ReactiveObjC 是一套基于Cocoa 的响应式编程框架。它提供了一套简介的 API 处理各种复杂消息传递问题,在使用 ReactiveObjC 开发的时候, 我们很容易用它来实现,View-Model 的绑定、用户交互事件绑定、状态依赖等问题。
RACStream
在整个 ReactiveObjC 中 RACStream 是一个抽象类,一般不直接使用。它有两个比较重要的子类 RACSignal、RACSequence。今天我们主要关注 RACSignal。RACSignal 可以说是 ReactiveObjC 中的核心类,也是 RAC 最重要的概念,整个框架围绕着 RACSignal 的概念进行组织,RACSignal 简单粗暴的理解就是它表示一连串的状态。当状态改变时,它的订阅者就会收到相应的消息。
RACStream.png信号的创建
RACSignal 使用类簇的设计模式,我们可以使用 RACSignal 相关类族,这些类族都继承自RACSignal,具体如下:RACDynamicSignal、RACEmptySignal、RACErrorSignal、RACReturnSignal :
RACSignal.png在创建信号的时候一般使用 + (RACSignal<ValueType> *)createSignal:(RACDisposable * _Nullable (^)(id<RACSubscriber> subscriber))didSubscribe
方法。
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@1];
[subscriber sendNext:@2];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"%@", @"dispose");
}];
}];
信号的订阅
ReactiveObjC 中信号的订阅与信息的发送的类主要是 RACSubscriber。在订阅信号的时候,我们可以选择我们关心的信号,如:next、error、completed。我们主要是通过 - (RACDisposable *)subscribeNext:error:completed:
来订阅信号。
@weakify(self);
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
@strongify(self)
self.subscriber = subscriber;
[subscriber sendNext:@"123"];
return [RACDisposable disposableWithBlock:^{
NSLog(@"%@", @"我被释放了");
}];
}];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
信息的发送
我们可以根据自己关注的消息进行发送,在 ReactiveObjC 中只要实现了RACSubscriber 协议的就可以发送消息。根据 RACSubscriber 协议,我们可以选择性的发送以下三种消息:
@protocol RACSubscriber <NSObject>
- (void)sendNext:(nullable id)value;
- (void)sendError:(nullable NSError *)error;
- (void)sendCompleted;
@end
订阅的回收
使用 + (RACSignal<ValueType> *)createSignal:(RACDisposable * _Nullable (^)(id<RACSubscriber> subscriber))didSubscribe
创建信号的时候,我们传入了 didSubscribe block,当订阅信号的时候,这个 block 便会执行并返回 RACDisposable 对象,用于取消正在执行的任务,以及订阅结束时的清理工作。
内存管理
之所以叫 RACSignal 与内存管理,是因为这篇文章不是入门文章,也不是源码阅读的文章,文章中只是简单介绍了 RACSignal 的创建与订阅过程,没从源码角度分析。不过既然涉及到 RACSignal 内存管理,因此,下面会从源码角度分析。
在使用 RACSignal 的时候,一般会考虑相关内存管理的问题,比如,1、RACSignal 什么时候释放;2、RACSubscriber 什么时候释放;3、RACDisposable 什么时候销毁;4、RACSignal 会不会导致循环引用。 以下是信号创建的相关引用关系。接下来一步步看相关内存管理。
RACSignal.png1、创建信号。我们使用 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
创建信号。用 RACSignal 创建信号的时候会调用 RACDynamicSignal
来创建信号。以下是源码内容:
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
return [RACDynamicSignal createSignal:didSubscribe];
}
RACDynamicSignal 创建信号的时候则 copy 了 didSubscribe。
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
RACDynamicSignal *signal = [[self alloc] init];
signal->_didSubscribe = [didSubscribe copy];
return [signal setNameWithFormat:@"+createSignal:"];
}
2、订阅信号。我们使用 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
订阅信号,这时候会创建 RACSubscriber 对象,并对 nextBlock 进行 copy。源码如下:
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL);
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o];
}
创建信号后,会调用 [self subscribe:o];
方法,这里的 self 是 之前创建的 RACDynamicSignal。在 - (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber
中会生成 RACPassthroughSubscriber 对象,该对象持有 RACDynamicSignal 和 RACCompoundDisposable 对象。在生成 RACPassthroughSubscriber 对象后,将其传入 RACDynamicSignal 持有的 didSubscribe Block 中去执行信号的发送工作。
- (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;
}
- 初步印象:
整个过程很像 RACDynamicSignal 持有 _didSubscribe Block,而 _didSubscribe Block 则持有 RACPassthroughSubscriber 对象,RACPassthroughSubscriber 的 _signal 属性又持有 RACDynamicSignal,因此,整个过程好像是循环引用。其实并不是,一方面 RACPassthroughSubscriber 的 _signal 属性并不是强引用,它的定义如下:
@property (nonatomic, unsafe_unretained, readonly) RACSignal *signal;
因此,整个消息创建与订阅的过程,并不会构成循环引用;另一方面,使用 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
方法创建信号时,虽然 copy 了 didSubscribe Block, 但是 RACDynamicSignal 并不会持有 id<RACSubscriber> subscriber 对象,因为, id<RACSubscriber> subscriber 只是参数,不是外部变量,因此Block copy 的时候并不会强引用 id<RACSubscriber> subscriber 。
- 案例:
1、如果我们不持有 RACDynamicSignal 和 RACPassthroughSubscriber ,则对象出了作用域之后会自动销毁。
- (void)viewDidLoad
{
[super viewDidLoad];
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"123"];
return [RACDisposable disposableWithBlock:^{
NSLog(@"%@", @"我被释放了");
}];
}];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
}
输出如下:
RAC[5280:524304] 123
RAC[5280:524304] -[RACPassthroughSubscriber dealloc]
RAC[5280:524304] -[RACSubscriber dealloc]
RAC[5280:524304] 我被释放了
RAC[5280:524304] -[RACCompoundDisposable dealloc]
RAC[5280:524304] -[RACCompoundDisposable dealloc]
RAC[5280:524304] -[RACDynamicSignal dealloc]
2、如果我们持有 RACDynamicSignal,但是 RACPassthroughSubscriber 并不会被强引用,因此,RACPassthroughSubscriber 会被释放。我们持有的 RACDynamicSignal 不会被释放。
- (void)viewDidLoad
{
[super viewDidLoad];
// @weakify(self);
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"123"];
self.subscriber = nil;
return [RACDisposable disposableWithBlock:^{
NSLog(@"%@", @"我被释放了");
}];
}];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
self.signal = signal;
}
输出如下:
RAC[5312:526033] 123
RAC[5312:526033] -[RACPassthroughSubscriber dealloc]
RAC[5312:526033] -[RACSubscriber dealloc]
RAC[5312:526033] 我被释放了
RAC[5312:526033] -[RACCompoundDisposable dealloc]
RAC[5312:526033] -[RACCompoundDisposable dealloc]
3、如果我们持有 RACPassthroughSubscriber,由于 RACPassthroughSubscriber 的属性 _signal 被 unsafe_unretained 修饰,因此,RACDynamicSignal 会被释放,RACCompoundDisposable 由于被 RACPassthroughSubscriber 持有,不会被释放。由于信号发送与接收只依赖 RACPassthroughSubscriber,因此,可以发送与接收信号。但是不能订阅信号(RACDynamicSignal被释放)。
- (void)viewDidLoad
{
[super viewDidLoad];
@weakify(self);
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"123"];
@strongify(self);
self.subscriber = subscriber;
return [RACDisposable disposableWithBlock:^{
NSLog(@"%@", @"我被释放了");
}];
}];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
}
输出如下:
RAC[5360:529305] 123
RAC[5360:529305] -[RACDynamicSignal dealloc]
4、类似上面的第3条,但是我们最后 dispose 信号,这时我们 didSubscribe 中返回的 RACDisposable 被 dispose,RACPassthroughSubscriber 不会释放,要使让它释放,不持有它便可。
- (void)viewDidLoad
{
[super viewDidLoad];
@weakify(self);
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"123"];
@strongify(self);
self.subscriber = subscriber;
return [RACDisposable disposableWithBlock:^{
NSLog(@"%@", @"我被释放了");
}];
}];
RACDisposable *disposable = [signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
[disposable dispose];
}
输出结果为:
RAC[5637:543032] 123
RAC[5637:543032] 我被释放了
RAC[5637:543032] -[RACDynamicSignal dealloc]
5、循环引用的情况,1、如果你的对象持有 id<RACSubscriber> subscriber,而 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
的 nextBlock 又持有你的对象;2、如果你的对象持有 RACSignal,而 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
的 didSubscribe 又持有你的对象。
情景1:
- (void)dealloc
{
NSLog(@"%s", __func__);
}
- (void)viewDidLoad
{
[super viewDidLoad];
@weakify(self);
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"123"];
@strongify(self);
self.subscriber = subscriber;
return [RACDisposable disposableWithBlock:^{
NSLog(@"%@", @"我被释放了");
}];
}];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
self.name = x;
}];
// 事件
[[_loginButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
UIViewController *vc = [[UIViewController alloc] init];
[[UIApplication sharedApplication] keyWindow].rootViewController = vc;
}];
}
情景2:
- (void)dealloc
{
NSLog(@"%s", __func__);
}
- (void)viewDidLoad
{
[super viewDidLoad];
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"123"];
self.name = @"123";
return [RACDisposable disposableWithBlock:^{
NSLog(@"%@", @"我被释放了");
}];
}];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
self.signal = signal;
// 事件
[[_loginButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
UIViewController *vc = [[UIViewController alloc] init];
[[UIApplication sharedApplication] keyWindow].rootViewController = vc;
}];
}
总结
对于 RACSignal 的内存管理还是比较简单,其它复杂的订阅也是基于 RACSignal 的,因此,理解 RACSignal 的内存管理,对熟悉其它复杂订阅方式的内存管理很有帮助。
网友评论