iOS 底层 被__block修饰的对象类型 - AlvinSunny/OC-TheUnderlying GitHub Wiki

被__block修饰的对象会发生什么 ?

  • 当__block变量在栈上时,不会对指向的对象产生强引用

  • 当__block变量被copy到堆上时

  1. 会调用__block变量内部的copy函数
  2. copy函数内部会调用_Block_object_assign函数
  3. _Block_object_assign函数会根据所指向对象的修饰符(__strong、__weak、__unsafe_umrefained)做出相应操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC环境会retain, MRC是不会retain,就是说一直都是弱引用)
  • 如果__block变量从堆上移除
  1. 会调用block内部的dispose函数
  2. dispose函数内部会调用_Block_object_dispose函数
  3. _Block_object_dispose函数会自动释放(对 __block 修饰的变量进行一次release操作,引用计数器减一;注意只是引用计数器减一不一定达到释放条件)引用的 __block 变量

##代码演示

源代码

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
    __block    XYHPerson *person = [[XYHPerson alloc] init];
        
        XYHBlock block = [^{
            NSLog(@"%p", person);
        } copy];
                
        block();
    }
    return 0;
}

转c++后的核心代码

struct __Block_byref_person_0 {
  void *__isa; // 8
__Block_byref_person_0 *__forwarding; // 8
 int __flags; // 4
 int __size; // 4
 void (*__Block_byref_id_object_copy)(void*, void*); // 8
 void (*__Block_byref_id_object_dispose)(void*); // 8
 XYHPerson *__strong person;   -- > block内部对person对象是请引用
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_person_0 *person; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_person_0 *_person, int flags=0) : person(person->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

图解

QQ20200319-222332@2x.png

如果在使用__block时用 __weak 修饰 person 对象 , __Block_byref_person_0 内部对其是强引用吗 ?

答:不是,是弱引用;

QQ20200319-223753@2x.png

MRC是不会retain,就是说一直都是弱引用说明(默认block内部访问的对象是经过__block修饰的)

QQ20200324-165507@2x.png

疑问:ARC 环境 如果去掉__block修饰person还会提前释放吗 ?为什么 ?有什么风险吗 ?

答: 不会提前释放; 因为不使用__block修饰时,block内部就是直接访问person对象,person会在main函数执行完释放; 在这段简单的代码中是没有风险的,但是在日常开发中若person是当前类的属性时,直接使用可能会造成循环引用。