Block经典问题循环引用&解决
Block内存关系
Block经典问题循环引用&解决
Block底层分析
Block底层HooK
1.循环引用怎么产生的?
因为block的特性,声明block的对象都会以copy的形式持有block,而block对于它内部的对象也会进行强引用,从而导致了循环引用。
简单来说就是在堆上的对象与堆上的对象互相引用造成的环,就会引起循环引用。
2.OC中block在内存中是存放在什么位置?
-
NSConcreteGlobalBlock
全局block,存储在数据区,不会引起循环引用的问题。(因为没有捕获外部变量) -
NSConcreteStackBlock
存储在栈上的block,内存管理由系统处理,不会引起循环引用问题 -
NSConcreteMallocBlock
这种block在堆上,如果捕获了外部对象,则会引起循环引用的问题。
3.关键字__block 、__weak、__strong的作用
__block 修饰的变量可以在block中被修改,具体实现细节很多大神都有讲解,各位同学可以先去看看内部原理,这里主要讲__weak、__strong在使用中的细节
注意:在ARC下,只要将block赋值给一个变量,那么这个block就将被拷贝到堆上,这是编译器的优化。所以,这种情况下,block被强引用,block又对捕获的对象强引用,我们就必须使用__weak来打破循环
__weak大家都很熟悉,对对象弱引用,以打破引用循环,解决问题。Weak原理请看这里
但是使用__weak有一个问题,可能导致在block执行的过程中对象被释放了,这就是一个很严重的问题,如果在block之前被释放,还可以接受。
下面看一个例子:这里可以看到,在执行block的时候,p被意外的释放了。
@interface Persion : NSObject
@property (nonatomic, copy)void(^block)();
@end
@implementation Persion
- (void)dealloc {
NSLog(@"%s",__func__);}
- (instancetype)init {
self = [super init];
if (self) {
__weak typeof(self) weakSelf = self;
self.block = ^{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"%@",weakSelf);
});
};
}
return self;
}
@end
-(void)test{
Persion *obj = [Persion new];
obj.block();
}
结果是打印的是(null),因为block里打印的方法是异步执行的,在 NSLog(@"%@",weakSelf);这句代码执行之前test函数就结束,所以obj对象已经被release了。
怎么解决呢?所以再对weakSelf做一次 __strong就可以了:
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"%@",strongSelf);
});
};}
使用了__strong在strongSelf变量作用域结束之前,对weakSelf有一个引用,防止对象(self)提前被释放。而作用域一过,strongSelf不存在了,对象(self)也会被释放。
4.循环引用解决方法
1.__weak弱引用
2.@weakify , @strongify 这两个宏定义,参考这里