selector

Block经典问题循环引用&解决

2019-08-01  本文已影响0人  6ffd6634d577

Block内存关系
Block经典问题循环引用&解决
Block底层分析
Block底层HooK

1.循环引用怎么产生的?

因为block的特性,声明block的对象都会以copy的形式持有block,而block对于它内部的对象也会进行强引用,从而导致了循环引用。

简单来说就是在堆上的对象与堆上的对象互相引用造成的环,就会引起循环引用。

2.OC中block在内存中是存放在什么位置?

  1. NSConcreteGlobalBlock
    全局block,存储在数据区,不会引起循环引用的问题。(因为没有捕获外部变量)
  2. NSConcreteStackBlock
    存储在栈上的block,内存管理由系统处理,不会引起循环引用问题
  3. 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 这两个宏定义,参考这里

上一篇下一篇

猜你喜欢

热点阅读