iOS block中使用self的那么事

前言

我们在使用block的时候,如果在block中使用self有可能会循环引用,产生内存泄漏的问题。

通常,我们如果遇到这种情况,我们会将self转换成weak automatic的变量,这样就避免了blockself强引用,即:

__weak typeof(self) weakSelf = self;
__weak __typeof__(self) weakSelf = self;
__weak __typeof(self) weakSelf = self;

以上三种写法,任选一种都是可以的,typeof就是获取变量的类型,没别的深奥的东西。

但是,我在查看AFNetworking源码的时候发现发现,在有的block中使用self,作者并没有将self转化weakSelf,难道这样不会引起内存泄漏的问题吗?

为此,我做了相关的测试。讲道理,如果存在内存泄漏,那么dealloc方法就不会被调用。

测试一:

@property (copy, nonatomic) dispatch_block_t testBlock;

- (void)viewDidLoad {
    [super viewDidLoad];

    self.testBlock = ^(){
        NSLog(@"%@", [self class]);
    };
    self.testBlock();
}

这块我为了简单,直接使用GCD中的

typedef void (^dispatch_block_t)(void)

它是一个无参数,无返回值的block
这个testBlock是控制器(以下用VC代替)的一个属性。在block中直接使用self就会造成循环引用,Xcode也会做出相应的警告提示:

⚠️ Capturing self strongly in this block is likely to lead to a retain cycle.

这句话的意思就是说,此处的block强引用了self,会存在保留环,即循环引用,那么VC的dealloc方法也不会被正常调用。这个时候我们就需要将其转化为weakSelf来打破这个保留环,避免内存泄漏。代码更正如下:

- (void)viewDidLoad {
    [super viewDidLoad];

    __weak typeof(self) weakSelf = self;

    self.testBlock = ^(){
        NSLog(@"%@", [weakSelf class]);
    };
    self.testBlock();
}

这样VC的dealloc方法也可以正常被调用了。

  • 结论:当block直接做为VC的属性时,如果block内部没有使用weakSelf,则会造成循环引用,导致内存泄漏。

测试二:

- (void)viewDidLoad {
    [super viewDidLoad];

    __weak typeof(self) weakSelf = self;

    self.testBlock = ^(){
        [weakSelf doSomething];
    };
    self.testBlock();
}

- (void)doSomething {
    NSLog(@"%@", [self class]);
}

测试发现VCdealloc方法被正常调用。

结论:当在block中调用一个方法,并且这个方法中直接或者间接的使用self,不会出现内存泄漏的问题。

测试三:

- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_block_t block = ^(){
        [self doSomething];
    };
    block();
}

- (void)doSomething {
    NSLog(@"%@", [self class]);
}

测试发现VC的dealloc方法被正常调用,所以我们在使用GCD的时候,大部分情况都不需要做转换。

结论:当block不是self的属性时,block内存使用self不会造成内存泄漏的问题。

测试四:

- (void)viewDidLoad {
    [super viewDidLoad];

    Class VC = self.class;
    [VC doSomethingWithBlock:^{
        [self doSomething];
    }];
}

+ (void)doSomethingWithBlock:(dispatch_block_t)block {
    if (block) {
        block();
    }
}

- (void)doSomething {
    NSLog(@"%@", [self class]);
}

测试发现VC的dealloc方法被正常调用,我们在使用UIView有关动画的类方法时,大部分情况都不需要做转换。

结论:当使用类方法,并且类方法中用block做参数时,block内部使用self也不会造成内存泄漏的问题。

通过这么多的测试,我们可以看到,当且仅当block直接或间接的被self持有时,如果不做weakSelf转换,就会有内存泄漏的风险。

最后补充一点,同样在查看AFNetworking的时候,遇到有时候还需要转化成strongSelf的情况:

__weak __typeof(self) weakSelf = self;

NSURLSessionDataTask *dataTask = nil;
dataTask = [self.sessionManager GET:request.URL.absoluteString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        __strong __typeof(weakSelf) strongSelf = weakSelf;
        sf_dispatch_main_async_safely(^{
            if (success) {
                success((NSHTTPURLResponse *)task.response, responseObject);
            }
            [strongSelf loadData:responseObject MIMEType:MIMEType textEncodingName:textEncodingName baseURL:task.currentRequest.URL];
            if ([strongSelf.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
                [strongSelf.delegate webViewDidFinishLoad:strongSelf];
            }
        });
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        sf_dispatch_main_async_safely(^{
            if (failure) {
                failure(error);
            }
        });
  }];

那么什么情况下,需要将weakSelf转化成strongSelf呢 ?

由于__weak变量的特殊性,会在对象销毁后自动置为nil,如果在block中多次需要访问self,就需要转化为strong automatic,确保在block使用期间,self不会被释放。

猜你喜欢

转载自blog.csdn.net/weixin_34050005/article/details/87241733