jianghui1 / TestRACSubscriber

RACSubscriber在订阅中的生命周期

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

TestRACSubscriber

RACSubscriber在订阅中的生命周期

这一篇分析过 RACScriber 的生命周期,今天发现了一个新的问题,重新分析下在耗时操作中 RACScriber 的声明周期。

下面的完整测试用例在这里

先看下正常情况信号订阅的例子:

- (void)test1
{
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:nil];
        [subscriber sendCompleted];
        
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"结束了");
        }];
    }];
    
    [signal subscribeNext:^(id x) {
        NSLog(@"test1 -- %@", x);
    }];
    
    // 打印日志
    /*
     2018-11-14 18:22:26.925204+0800 TestRACSubscriber[7055:2380811] test1 -- (null)
     2018-11-14 18:22:26.925453+0800 TestRACSubscriber[7055:2380811] 结束了
     */
}

这样子并没有什么问题,信号正常订阅,正常结束。

但是,一般 app 中都会使用网络请求,就会出现耗时的操作,所以,再看下耗时情况下的例子:

- (void)test2
{
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        [[RACScheduler mainThreadScheduler] afterDelay:0.3 schedule:^{
            
            [subscriber sendNext:nil];
            [subscriber sendCompleted];
        }];
        
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"结束了");
        }];
    }];
    
    [signal subscribeNext:^(id x) {
        NSLog(@"test2 -- %@", x);
    }];
    
    [[RACSignal never] asynchronouslyWaitUntilCompleted:nil];
    
    // 打印日志
    /*
     2018-11-14 18:25:56.233875+0800 TestRACSubscriber[7201:2383360] test2 -- (null)
     2018-11-14 18:25:56.234550+0800 TestRACSubscriber[7201:2383360] 结束了
     */
}

这时,使用了延时模拟了网络请求的耗时操作,并对这个操作对应的清理对象做处理。这样跟正常使用网络请求是一样的。查看结果,一切正常。

接着,对上面例子继续改造:

- (void)test3
{
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        RACDisposable *d = [[RACScheduler mainThreadScheduler] afterDelay:0.3 schedule:^{
            
            NSLog(@"test3 -- xxx");
        }];
        
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"结束了");
            [d dispose];
        }];
    }];
    
    [signal subscribeNext:^(id x) {
        NSLog(@"test3 -- %@", x);
    }];
    
    [[RACSignal never] asynchronouslyWaitUntilCompleted:nil];
    
    // 打印日志
    /*
     2018-11-14 18:33:02.400545+0800 TestRACSubscriber[7474:2387962] 结束了
     */
}

同样是使用延时模拟网络请求的耗时操作,不过并没有对结果进行发送。这个时候,延时操作并没有最终执行,相当于网络请求被取消了。为什么呢?延时操作不执行,就是被清理了,证明代码执行了 [d dispose]; ,也就是订阅过程结束了。之前文章说过,RACSubscriber 负责信号的分发,所以这里 RACSubscriber 已经释放了。为什么会释放呢?之前文章也说了,RACSubscriber 是作为局部变量存在的,所以会释放。那么有人就要说了,第二个例子为何不释放呢?别急,再看下例子:

- (void)test4
{
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        RACDisposable *d = [[RACScheduler mainThreadScheduler] afterDelay:0.3 schedule:^{
            
            NSLog(@"test4 -- xxx -- %@", subscriber);
        }];
        
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"结束了");
            [d dispose];
        }];
    }];
    
    [signal subscribeNext:^(id x) {
        NSLog(@"test4 -- %@", x);
    }];
    
    [[RACSignal never] asynchronouslyWaitUntilCompleted:nil];
    
    // 打印日志
    /*
     2018-11-14 18:39:51.437398+0800 TestRACSubscriber[7715:2391572] test4 -- xxx -- <RACPassthroughSubscriber: 0x600000f6f7a0>
     2018-11-14 18:39:51.437689+0800 TestRACSubscriber[7715:2391572] 结束了
     */
}

延时操作又执行了。到这里,就可以知道问题了。正常的信号订阅,RACSubscriber 作为局部变量没错,被释放也没错,但是这里 subscriber 作为变量被延时操作的 block 引用了,所以就不会立即释放了,只有延时正常结束才释放,所以延时操作就能够正常执行了。对于 app 中,也就是网络请求可以正常请求了。

其实,如果你创建信号的时候,不通过 subscriber 做任何事情的话,那跟创建一个 never 信号没有什么差别。

当然,如果按照信号的标准创建的话,一定不会有问题,因为你必定会通过 subscriber 发送 sendError: 或者 sendCompleted 事件,这时耗时操作中就会对 subscriber 引用,就不会过早释放了。

About

RACSubscriber在订阅中的生命周期

License:MIT License


Languages

Language:Objective-C 98.9%Language:Ruby 1.1%