美文网首页程序员
RACSignal与内存管理

RACSignal与内存管理

作者: 秦明Qinmin | 来源:发表于2017-11-08 06:51 被阅读420次

简介

相信不用过多介绍都知道 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 对象,用于取消正在执行的任务,以及订阅结束时的清理工作。

RACDisposable.png

内存管理

之所以叫 RACSignal 与内存管理,是因为这篇文章不是入门文章,也不是源码阅读的文章,文章中只是简单介绍了 RACSignal 的创建与订阅过程,没从源码角度分析。不过既然涉及到 RACSignal 内存管理,因此,下面会从源码角度分析。

在使用 RACSignal 的时候,一般会考虑相关内存管理的问题,比如,1、RACSignal 什么时候释放;2、RACSubscriber 什么时候释放;3、RACDisposable 什么时候销毁;4、RACSignal 会不会导致循环引用。 以下是信号创建的相关引用关系。接下来一步步看相关内存管理。

RACSignal.png

1、创建信号。我们使用 + (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 的内存管理,对熟悉其它复杂订阅方式的内存管理很有帮助。

相关文章

  • RACSignal与内存管理

    简介 相信不用过多介绍都知道 ReactiveObjC 是一套基于Cocoa 的响应式编程框架。它提供了一套简介的...

  • ReactiveCocoa用法示例(二)

    知识点 RACSignal与OC对象方法的绑定 RACSignal与OC对象属性的绑定 [RACSignal me...

  • 操作系统之内存管理

    内存管理 包括内存管理和虚拟内存管理 内存管理包括内存管理概念、交换与覆盖、连续分配管理方式和非连续分配管理方式(...

  • RACSubject

    1.RACSubject继承RACSignal,与RACSignal不同的是,RACSubject自身就可以信号的...

  • 内存与内存管理

    前言 对于大多数开发者,特别是 C, Objective-C, Swift 等相关的开发者来说,已经很了解如何避免...

  • 可能是史上最全面的内存管理文章

    iOS内存管理 概述 什么是内存管理 应用程序内存管理是在程序运行时分配内存(比如创建一个对象,会增加内存占用)与...

  • ReactiveCocoa框架学习及资源整理

    常用类 RACSignal类 RACSignal(Subscription) RACSignal(RACStrea...

  • 内存管理

    一、内存管理 1.内存管理概念 -程序装入与链接;逻辑地址与物理地址空间;内存保护 2.交换与覆盖 3.连续分配管...

  • iOS/OS X内存管理(一):基本概念与原理

    iOS/OS X内存管理(一):基本概念与原理 iOS/OS X内存管理(一):基本概念与原理

  • 【操作系统】3.1 内存管理基础

    1.内存管理的介绍 问题:操作系统的内存管理主要是做什么? 操作系统的内存管理主要负责①内存的分配与回收(mall...

网友评论

    本文标题:RACSignal与内存管理

    本文链接:https://www.haomeiwen.com/subject/pdvcmxtx.html