美文网首页
Block循环引用

Block循环引用

作者: 小心韩国人 | 来源:发表于2019-01-22 15:27 被阅读8次

一说起Block,肯定就要说它的循环引用.这是个老生常谈的问题,也是我们面试中经常被问到的.循环引用就说:我中有你,你中有我.如图所示:


循环引用关系

今天我们将会通过底层代码,更加深刻的了解循环引用.

typedef void (^HHBlock) (void);

@interface Person : NSObject
@property (nonatomic,copy)HHBlock block;
@property (nonatomic,assign)int age;

@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {

        Person *person = [[Person alloc]init];
        person.age = 20;
        person.block = ^{
            NSLog(@"age is %d",person.age);
        };
        NSLog(@"-----------------");
    }
    return 0;
}

我们在Person类中添加HHBlock , age两个属性,然后再 main 函数中访问 age 属性会怎么样呢?很显然这样会发生循环引用,我们在开发中也经常遇到这样的问题.他们之间的引用关系如下图:


查看底层代码发现block内部的确对 person 进行了强引用:
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Person *__strong person; // 对 person 产生了强引用
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _person, int flags=0) : person(_person) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

那我们怎么解决循环引用呢?我们可以试着分析一下,如下图所示,Block 内部和 Person 产生了互相强引用,那我们只要把其中的一条线变成弱引用就可以了.


如果把第二条线变成弱引用,那么就会存在 person 对象还没销毁,block 就提前释放的情况,可能会影响 block 的正常使用,所以我们还是要 对 _block 成员变量保持强引用.所以最佳的解决办法就是把第一条线变成弱引用.
  • 在ARC环境下,有两种方法可以解决这种循环引用的问题:
    1: __weak
    2:__unsafe_unretained

我们使用 __weak 然后看一下底层代码:

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        Person *person = [[Person alloc]init];
        __weak Person *weakPerson = person;
        person.age = 20;
        person.block = ^{
            NSLog(@"my age is %d",weakPerson.age);
        };
        NSLog(@"-----------------");
        person.block();
    }
    return 0;
}
=====================================================

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Person *__weak weakPerson; // block 内部对person的引用的确变成了弱引用
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

__weak转换成__unsafe_unretained再看看:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Person *__unsafe_unretained weakPerson; // 底层已经转换为 __unsafe_unretained
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__unsafe_unretained _weakPerson, int flags=0) : weakPerson(_weakPerson) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

发现底层已经变成__unsafe_unretained,好尴尬,看不出来效果,我们运行一下看看效果:


既然__weak 和 __unsafe_unretained都是弱引用,那么他们之间有什么区别呢?
从名字我们可以看出来__unsafe_retained是不安全的,所谓的不安全就是:如果 Person 对象释放了,使用在 block 内部会把使用__weak修饰的 person 指针设置为 nil,而不会把使用__unsafe_retained修饰的指针设置为nil,所以会出现野指针的情况.
  • 使用 __block也可以解决循环引用的情况.
    使用__block解决循环引用
    使用__block修饰person后会出现3个对象Person对象,Block对象(__main_block_impl_0),__block对象(__Block_byref_weakPerson_0),他们之间的关系如下:
    __block对象的循环引用关系
    如上图所示,他们三者之间互相强引用,已经形成了一个⭕️,如果要使用__block就要及时切断切断一条线:
int main(int argc, const char * argv[]) {
    @autoreleasepool {

        Person *person = [[Person alloc]init];
        __block Person *weakPerson = person;
        person.age = 20;
        person.block = ^{
            NSLog(@"my age is %d",weakPerson.age);
//在 block 内部,把 person 对象置为nil
            weakPerson = nil;
        };
        NSLog(@"-----------------");
        person.block();
    }
    return 0;
}

运行以上代码发现 person 对象成功释放了,说明他们三者之间的循环引用被打断了.我们思考一下,在 block 内部把 person 置为nil,切断的是哪根线呢?

总结:

有三种方法可以解决 Block 的循环引用问题
1: __weak
2: __unsafe_unretained
3: __block
一般我们都使用__weak,因为__unsafe_unretained是不安全的,使用__block的前提条件是 Block 必须要调用,并且要在 Block内部把对象置为 nil.

以上讲的都是 ARC环境下,如果在 MRC环境下怎么解决 Block 循环引用问题呢?

因为在 MRC 环境下没有__weak关键字,所以在 MRC 下有两种解决方式:
1: __unsafe_unretained
2: __block
需要注意的是,在 MRC 下, __blcok 对象内部是不会强引用外部对象的,所以在 MRC 下不需要把对象置为nil,也不是必须调用 blcok.

相关文章

网友评论

      本文标题:Block循环引用

      本文链接:https://www.haomeiwen.com/subject/svfljqtx.html