iOS 中block的循环引用问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/aaaaazq/article/details/85123590

开发中经常使用weakSelf和strongSelf来解决block的循环引用问题,但是是不是所有的block都会导致循环引用呢?显然不是的,那么怎么判断调用一个带有block方法时是否会造成循环引用呢,我们来分析一下。
首先我们来写一个含有block的类,并调用自己,然后在外部实现这个block,来测试什么情况会出现循环引用。

@interface ALDTestBlockModel ()

@property (nonatomic, copy) dispatch_block_t testBlock;
@property (nonatomic, copy) NSString * testString;

@end

@implementation ALDTestBlockModel
- (void)initWithBlock1:(dispatch_block_t)block{
    //对象不持有该block
    if (block) {
        block();
    }
}

- (void)initWithBlock2:(dispatch_block_t)block{
    //对象持有该block
    self.testBlock = block;
    if (self.testBlock) {
        self.testBlock();
    }
}
@end

外部测试代码:

@interface ALDTestBlockController ()

@property (nonatomic, strong) ALDTestBlockModel *model;

@end

@implementation ALDTestBlockController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.model = [ALDTestBlockModel new];
    [self noSelfTest];
    //[self haveSelfTest];
}

- (void)noSelfTest{
    [self.model initWithBlock1:^{
        NSLog(@"1111self=[]");
    }];
    [self.model initWithBlock2:^{
        NSLog(@"2222self=[]");
    }];
}

- (void)haveSelfTest{
    [self.model initWithBlock1:^{
        NSLog(@"1111self====== %@",self.testString);
    }];
    [self.model initWithBlock2:^{
        NSLog(@"2222self====== %@",self.testString);
    }];
}

- (void)dealloc{
    NSLog(@"");
}

@end

我们首先来测试在实现block中没有出现self及成员变量之类的,我们断点在block类中,可以看一下block和testBlock的isa,我们会发现block和testBlock的isa都是一个NSGlobalBlock类型,然后在controller点击返回,断点在dealloc,会发现会走断点。

然后来测一下block中出现self或者成员变量时,先测

[self.model initWithBlock1:^{
        NSLog(@"1111self====== %@",self.testString);
    }];

这个方法里面,未持有block,点击返回,dealloc的断点依然会走。
再来测一下

[self.model initWithBlock2:^{
        NSLog(@"2222self====== %@",self.testString);
    }];

这个方法里面,model持有了block,点击返回不会走dealloc,说明出现了循环引用。这个时候就可以用weakSelf和strongSelf来解决循环引用的问题了。
总结一下,当调用带有block的方法时,如果该方法内部没有持有该block,那么这个方法在被调用时,就不需要考虑循环引用的问题。如果该方法内部持有了该block,那么这个方法在被调用时,一定要注意block实现时是否会用到self或者self相关的,如果要用到,就需要用weakSelf和strongSelf来避免循环引用。
最后补充一点,block的isa有三种:

  • NSGlobalBlock:存储在程序的数据区域,在 block 内部没有引用任何外部变量。
  • NSStackBlock:存储在栈上,在 block 内部引用外部变量。在 MRC 下,栈块在当函数退出的时候,该空间会被回收,因此如果再调用该 block 会导致 crash,通过拷贝该栈块,可以解决该问题。在 ARC 模式下,生成的 block 也是 栈块,只是当赋值给 strong 对象时,系统会主动对其进行 copy。
  • NSMallocBlock:存储在堆上的 block。
    我们都知道栈区的内存是系统自动管理的,所以出现NSStackBlock时我们可以不用考虑内存问题,但是出现NSMallocBlock时一定要注意了,很容易出现内存泄漏。

猜你喜欢

转载自blog.csdn.net/aaaaazq/article/details/85123590