IOS中block循环引用的诡异问题

146 阅读1分钟

我们先来一个Demo

#import <Foundation/Foundation.h>

#define WEAKSELF() __weak __typeof(self) weakSelf = self;
#define STRONGSELF() if(!weakSelf) return ; __strong __typeof(weakSelf) self = weakSelf;

@interface DemoObject :NSObject
{
    void(^block)(void);
}

@property (strong, nonatomic)NSString *myname;

@end
@implementation DemoObject

- (void)test
{
    WEAKSELF()
    block = ^void(void){
        STRONGSELF()
        NSLog(@"%@",self.myname);
    };
    block();
}

- (void)dealloc
{
    NSLog(@" DemoObjet dealloc!!");
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        DemoObject *demoObj = [DemoObject new];
        demoObj.myname = @"hello!";
        [demoObj test];
    }
    [[NSRunLoop currentRunLoop] run];
    return 0;
}

运行结果

2022-06-19 17:17:18.082107+0800 BlockSelf[1847:53701] hello!
2022-06-19 17:17:18.082606+0800 BlockSelf[1847:53701]  DemoObjet dealloc!!

DemoObjet被释放了,说明没有引起循环引用。主要是STRONGSELF宏将声明了一个self的局域变量,解除了循环引用链。

接下来我们将代码改一下

- (void)test
{
    WEAKSELF()
    block = ^void(void){
        STRONGSELF()
        NSLog(@"%@",_name); //这里改成_name
    };
    block();
}

输出结果

2022-06-19 17:18:39.747908+0800 BlockSelf[1861:55035] hello!

DemoObjet没有释放。

也就是说我们在block里面将.self.name改成了_name导致了循环引用。

按理说_name 等同于self->_name ,而self已经被STRONGSELF 定义成了局部变量。那为什么会出现循环引用呢?

我们使用clang将OC转成C/C++代码

clang -rewrite-objc -fobjc-arc -fobjc-runtime=macosx-10.14 main.m

目录下会出现main.cpp

struct __DemoObject__test_block_impl_0 {
  struct __block_impl impl;
  struct __DemoObject__test_block_desc_0* Desc;
  DemoObject *const __weak weakSelf;		
  __DemoObject__test_block_impl_0(void *fp, struct __DemoObject__test_block_desc_0 *desc, DemoObject *const __weak _weakSelf, int flags=0) : weakSelf(_weakSelf) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

可以看到这句

DemoObject *const __weak weakSelf; 确实是__weak,那为什么还是会循环引用了呢?