美文网首页iOS开发
关于Block中的非典型循环引用

关于Block中的非典型循环引用

作者: 高浩浩浩浩浩浩 | 来源:发表于2023-09-04 12:12 被阅读0次

    今天遇到这样子一个场景,嵌套Block下的外层strongSelf是否会产生循环引用
    先看Demo 代码:

    - (void)testRetainCycle {
        __weak typeof(self) weakSelf = self;
        NSLog(@"B objc doStudent strong-----retainCount : %lu", CFGetRetainCount(((__bridge CFTypeRef)self)));
    
        self.doWork = ^{
            __strong typeof(self) strongSelf = weakSelf;
            NSLog(@"B objc doStudent strong-----retainCount : %lu", CFGetRetainCount(((__bridge CFTypeRef)strongSelf)));
    
            weakSelf.doStudent = ^{
                NSLog(@"B objc doStudent strong-----retainCount : %lu", CFGetRetainCount(((__bridge CFTypeRef)strongSelf)));
            };
            weakSelf.doStudent();
        };
        self.doWork();
    }
    

    先说结论: 这里会产生循环引用的
    并且是self和doStudent互相持有导致的循环引用。
    从memory graph 上看也确实是这样子:

    Xnip2023-09-05_11-12-54.jpg

    那么为什么呢?我们知道在ARC中:

    __weak:

    1. __weak用于声明弱引用,不会增加对象的引用计数。这是为了避免循环引用问题,常用于解决持有对象之间的强引用循环。
    2. 如果一个对象只有__weak引用,当没有强引用指向它时,对象会被释放,这可以防止循环引用。

    但是当我们在Block里面去用StrongSelf去持有一个weak引用的时候,会导致weak的引用计数+1,这么做一般是用来避免block内的对象被提前释放。

    那我们再回到这个代码里面, 在这里

    __strong typeof(self) strongSelf = weakSelf;
    

    weakSelf的引用计数+1, 可以理解为这里的doWork 持有了 strongSelf/ weakSelf。

    那么在weakSelf被+1的情况, 那么doStudent也被doWork持有了。

    那么引用关系就出现了, doStudent 里面又持有了 strongSelf。

    本来正常的情况是在doWork执行完成以后释放strongSelf的, 但是因为strongSelf被doStudent 持有了无法释放。

    weakSelf持有了doStudent, 因为weakSelf没有释放,所以doStudent 也无法释放了。
    这里形成了循环引用。

    下面是解决办法:
    - (void)testRetainCycle {
        __weak typeof(self) weakSelf = self;
        NSLog(@"B objc doStudent strong-----retainCount : %lu", CFGetRetainCount(((__bridge CFTypeRef)self)));
    
        self.doWork = ^{
            __strong typeof(self) strongSelf = weakSelf;
            NSLog(@"B objc doStudent strong-----retainCount : %lu", CFGetRetainCount(((__bridge CFTypeRef)strongSelf)));
    
            weakSelf.doStudent = ^{
                __strong typeof(self) strongSelf2 = weakSelf;
                NSLog(@"B objc doStudent strong-----retainCount : %lu", CFGetRetainCount(((__bridge CFTypeRef)strongSelf2)));
            };
            weakSelf.doStudent();
        };
        self.doWork();
    }
    

    这里在内层用另一个临时变量去引用weak,可以解决循环引用,从这里也能看出来循环引用的持有原因

    PS:这里用memory graph调试 内存问题真的很好用

    Using Xcode’s memory graph to find memory leaks

    相关文章

      网友评论

        本文标题:关于Block中的非典型循环引用

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