我们在使用block的时候,如果在block中使用self
有可能会循环引用,产生内存泄漏的问题。
通常,我们如果遇到这种情况,我们会将self
转换成weak automatic
的变量,这样就避免了block对sel
f强引用,即:
__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也会做出相应的警告提示:
⚠️
Capturingself
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]);
}
测试发现VC的dealloc
方法被正常调用。
结论:当在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
不会被释放。