美文网首页程序员
Block循环引用很有意思的一道题目

Block循环引用很有意思的一道题目

作者: Levi段玉磊 | 来源:发表于2018-06-15 00:53 被阅读9次

    疑问

    是在简书中看到的,原地址为:https://www.jianshu.com/p/6d6fd717390e

    例一:

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        Student *student = [[Student alloc]init];
        student.name = @"Hello World";
        
        __weak typeof(student) weakStu = student;
        Student *strongStu = weakStu;
        student.study = ^{
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                
                NSLog(@"my name is = %@", strongStu.name);
            });
        };
    }
    

    作者原话是:

    例1是有内存泄漏的没有走-[Student dealloc],因为未执行student.study, 所以dispatch_after block也不会走,但是dispatch_after bLock却强引用了strongStu还是发生了循环引用。这个比较好理解。但是下面例2,就改了一行代码 我怎么也想不通为什么 没有发生循环引用,走了-[Student dealloc] .

    例2:

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        Student *student = [[Student alloc]init];
        student.name = @"Hello World";
        
        __weak typeof(student) weakStu = student;
        student.study = ^{
             Student *strongStu = weakStu;
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                
                NSLog(@"my name is = %@", strongStu.name);
            });
        };
    }
    

    dispatch_after block 强引用了外部变量strongStu ,这种不调用student.study()的写法为什么没有循环引用呢,但是如果在ViwDidLoad结尾调用student.study(),那么会在2秒后执行完dispatch_after block才会走-[Student dealloc],不就说明dispatch_after block持有这个student,走完才回销毁,那如果不执行student.study()的话,按道理讲,应该也会被dispatch_after block持有这个student,为什么 不会产生循环引用呢

    匪夷所思如果有哪个大神路过,麻烦给个思路,我真想不明白。

    请问这个问题出在哪里?

    华丽的分割线,思考完再看我的理解。。

    解答

    首先说下你对例1场景解答的问题

    你的解答是:
    Q:例1是有内存泄漏的没有走-[Student dealloc],因为未执行student.study, 所以dispatch_after block也不会走,但是dispatch_after bLock却强引用了strongStu还是发生了循环引用。这个比较好理解。但是下面例2,就改了一行代码 我怎么也想不通为什么 没有发生循环引用,走了-[Student dealloc] .
    A:
    我先更正下你的解答
    1.例1的内存泄漏和执行以及未执行student.study没有任何直接关联,如果未执行student.study存在内存泄漏,那么执行也会存在内存泄漏。
    2.但是dispatch_after bLock却强引用了strongStu还是发生了循环引用,这里面dispatch_after只是干扰因素,和你的内存泄漏没有直接关联。你可以去掉它,也一样会内存泄漏,所以去年的你应该是理解错了,可能今年的你已经解决这个问题了,不过还是针对去年的你说一下问题的原因吧。

    Q:例2,dispatch_after block 强引用了外部变量strongStu ,这种不调用student.study()的写法为什么没有循环引用呢,但是如果在ViwDidLoad结尾调用student.study(),那么会在2秒后执行完dispatch_after block才会走-[Student dealloc],不就说明dispatch_after block持有这个student,走完才回销毁,那如果不执行student.study()的话,按道理讲,应该也会被dispatch_after block持有这个student,为什么 不会产生循环引用呢。
    A:无论例1还是例2 dispatch_after的block都不会强引用你的变量,强引用变量的是strongStu.name。无论是否调用student.study()和你的内存泄漏没有直接关联。

    我对例1的理解

    例1之所以会内存泄漏是由于,student类中的的方法study是用copy属性修饰的,因此在你调用student.study = ^{ }时,是将strongStu.name这个强引用赋值到了block的堆中,block本来就被strongStu所引用,block又同时引用了StrongStu,导致A,B成为双向引用的保留环。
    __weak typeof(student) weakStu = student;
    Student *strongStu = weakStu;
    这两句话一起写,虽然不明白什么意思,不过weakStu是弱引用,storngStu是强引用。
    如果你想打破这个环,很简单,就是用弱引用就可以了。
    把例一这句话改成 NSLog(@"my name is = %@", weakStu.name);就不会出现内存泄漏了。

    我对例2的理解

    例2,为什么没有循环引用?因为你在block里面将弱引用变成了强引用,这个你应该会懂吧?那为什么例1的强引用就会循环引用,例2就不会呢?很简单,因为例1的StrongStu是block外面的强引用,这个是引用这个block的强引用,而例2的block里面的的强引用是临时变量,在block结束后会自动销毁掉。应该是没理解block外面的强引用和block里面的强引用导致不明白这两个例子的区别。

    思考与行动:

    1. 为什么dispatch_after的block函数,不需要解决循环引用保留环的问题?
    2. 如果我们常常写网络请求时候,会用到成功的block回调或者失败的block回调,这里的block需要写@weakify或者@strongify么?

    相关文章

      网友评论

        本文标题:Block循环引用很有意思的一道题目

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