OC底层知识,Block访谈对象类型

在上篇小说中有提起ARC意况下,编写翻译器会依据事态自行将栈上的block拷贝到堆上,具体意况以下:

参考篇:iOS-Block浅谈

序言:本文简述Block本质,如有错误请留言指正。

  • block本质上也是一个OC对象,它个中也是有个isa指针
  • block是包装了函数调用以致函数调用境遇的OC对象
  • block是封装函数及其上下文的OC对象

图片 1block底层结构图

查看block源码:

struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr;};struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; // 构造函数(类似于OC的init方法),返回结构体对象 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};// 封装了block执行逻辑的函数static void __main_block_func_0(struct __main_block_impl_0 *__cself) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_c60393_mi_0); }static struct __main_block_desc_0 { size_t reserved; size_t Block_size;} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; // 定义block变量 void  = &__main_block_impl_0( __main_block_func_0, &__main_block_desc_0_DATA ); // 执行block内部的代码 block->FuncPtr; } return 0;}

图片 2clang查看Block

  • FuncPtr:指向调用函数的地点
  • __main_block_desc_0 :block描述新闻
  • Block_size:block的大小
int age=10;void  = ^{ NSLog(@"age:%d",age);};age = 20;Block();

输出值为 age:10缘故:创制block的时候,已经把age的值存储在里面了。

auto int age = 10;static int num = 25;void  = ^{ NSLog(@"age:%d,num:%d",age,num);};age = 20;num = 11;Block();

输出结果为:age:10,num:11甘当:auto变量block访问形式是值传递,static变量block访谈格局是指针传递源码评释:

int age = __cself->age; // bound by copyint *num = __cself->num; // bound by copyNSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_d2875b_mi_0, age, ;int age = 10;static int num = 25;block = &__test_block_impl_0__test_block_func_0, &__test_block_desc_0_DATA, age, &num));age = 20;num = 11;

上述代码可查看 static修饰的变量,是基于指针访谈的

auto自动变量可能会销毁的,内部存款和储蓄器大概会磨灭,不使用指针访问;static变量向来保存在内部存款和储蓄器中,指针访谈就可以

block无需对全局变量捕获,都以直接动用取值的

思索成效域的标题,需求跨函数访问,就供给捕获

为了保障block内部能够健康访问外部的变量,block有个变量捕获机制

图片 3block的变量捕获

会,self是当调用block函数的参数,参数是一对变量,self指向调用者

会,成员变量的拜访其实是self->xx,先捕获self,再通过self访谈里面包车型客车分子变量

block的花色,决意于isa指针,能够通过调用class方法也许isa指针查看具体品种,最后都是承接自NSBlock类型

  • __NSGlobalBlock __ ( _NSConcreteGlobalBlock )
  • __NSStackBlock __ ( _NSConcreteStackBlock )
  • __NSMallocBlock __ ( _NSConcreteMallocBlock )

代码示例

void  = ^{ NSLog(@"block1");};NSLog(@"%@",[block1 class]);NSLog(@"%@",[[block1 class] superclass]);NSLog(@"%@",[[[block1 class] superclass] superclass]);NSLog(@"%@",[[[[block1 class] superclass] superclass] superclass]);NSLog(@"%@",[[[[[block1 class] superclass] superclass] superclass] superclass]);

出口结果:NSGlobalBlock__NSGlobalBlockNSBlockNSObjectnull

上述代码输出了block1的品种,也印证了block是指标,最后承接NSObject

代码显示block的三种档期的顺序:

int age = 1;void  = ^{ NSLog(@"block1");};void  = ^{ NSLog(@"block2:%d",age);};NSLog(@"%@/%@/%@",[block1 class],[block2 class],[^{ NSLog(@"block3:%d",age);} class]);

出口结果:__NSGlobalBlock __/__NSMallocBlock __/__NSStackBlock __

  • __NSGlobalBlock __ 在数据区
  • __NSMallocBlock __ 在堆区
  • __NSStackBlock __ 在栈区
  • 堆:动态分配内部存款和储蓄器,须求程序猿本身报名,程序猿本身管理
  • 栈:自动分配内部存款和储蓄器,自动销毁,先入后出,栈上的从头到尾的经过存在活动销毁的场馆

图片 4block内部存款和储蓄器分配

  • 从没访谈auto变量的block是__NSGlobalBlock __ ,放在数据段
  • 访问了auto变量的block是__NSStackBlock __
  • [__NSStackBlock __ copy]操作就产生了__NSMallocBlock __
  • __NSGlobalBlock __ 调用copy操作后,什么也不做
  • __NSStackBlock __ 调用copy操作后,复制效果是:从栈复制到堆;别本存储地方是
  • __NSStackBlock __ 调用copy操作后,复制效果是:引用计数扩充;别本存款和储蓄地点是
  • 1.block充任函数重返值时
  • 2.将block赋值给__strong指针时
  • 3.block看成Cocoa API中方法名含有usingBlock的章程参数时
  • 4.block当做GCD API的不二等秘书籍参数时

MRC下block属性的提议写法@property (copy, nonatomic) void ;

ARC下block属性的建议写法@property (strong, nonatomic) void ;``@property (copy, nonatomic) void ;

示范代码:

typedef void(^XBTBlock);XBTBlock block;{ Person *p = [[Person alloc] init]; p.age = 10; block = ^{ NSLog(@"======= %d",p.age); };}Person.m- dealloc{ NSLog(@"Person - dealloc");}

输出结果:不会打印Person - dealloc转化C 代码后:

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; Person *person; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, MJPerson *__strong _person, int flags=0) : person { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};

上述block为堆block,block里面有一个Person指针,Person指针指向Person对象。只要block还在,Person就还在。block强引用了Person对象。

会的!堆空间的block会对Person对象retain操作,具备三遍Person对象。

@autoreleasepool { XBTBlock block; { Person *p = [[Person alloc] init]; p.age = 10; __weak Person *weakPersn = p; block = ^{ NSLog(@"======= %d",weakPersn.age); }; } NSLog(@"--------------");}

答案:会释放

当block使用外界变量时,是无法直接在block内修改那一个变量的。我们用__block修饰变量后就可见修改了。但必要证圣元(Synutra)些__block只可以用于auto变量非常小概修改,__block无法修饰全局变量、静态变量。

提醒:上面会把OC相应的类转化为C 代码,OC代码转C 代码的生成

  • block作为函数再次来到值时
  • 将block赋值给强指针时
  • block作为Cocoa API中方法名含有usingBlock的章程参数时
  • block作为GCD API的艺术参数时。

小结

无论MRC依旧ARC,栈空间上的block,不会怀有对象;堆空间的block,会怀有对象。

答案:分情形商量,分为栈block和堆block

栈blocka) 若是block是在栈上,将不会对auto变量发生强援引b) 栈上的block随即会被销毁,也没须求去强引用别的对象

堆block1.假设block被拷贝到堆上:a) 会调用block内部的copy函数b) copy函数内部会调用_Block_object_assign函数c) _Block_object_assign函数会基于auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,产生强引用只怕弱征引

2.纵然block从堆上移除a) 会调用block内部的dispose函数b) dispose函数内部会调用_Block_object_dispose函数c) _Block_object_dispose函数会自行释放引用的auto变量

正确答案:

  • 如果block在空间,不管外界变量是强引用依旧弱引用,block都会弱引用访谈对象
  • 如果block在空间,假诺外界强引用,block内部也是强援用;假诺外界弱引用,block内部也是弱援用

鸡犬不留方案:协助ARC、钦赐运维时系统版本,举个例子xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

-touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ Person *person = [[Person alloc] init]; person.age = 10; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"age:%d",person.age); }); NSLog(@"touchesBegan");}

出口结果:14:36:03.395120 0800 test[1032:330314] touchesBegan14:36:05.395237 0800 test[1032:330314] age:1014:36:05.395487 0800 test[1032:330314] Person-dealloc

案由:gcd的block暗中认可会做copy操作,即dispatch_after的block是堆block,block会对Person强援用,block销毁时候Person才会被放出。

-touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ Person *person = [[Person alloc] init]; person.age = 10; __weak Person *weakPerson = person; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"age:%p",weakPerson); }); NSLog(@"touchesBegan");}

出口结果:14:38:42.996990 0800 test[1104:347260] touchesBegan14:38:42.997481 0800 test[1104:347260] Person-dealloc14:38:44.997136 0800 test[1104:347260] age:0x0

原因:使用__weak修饰过后的对象,堆block会采纳弱援用,不能够延时Person的寿命,所以在touchesBegan函数截止后,Person就能够被放飞,gcd就不可能捕捉到Person。

-touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ Person *person = [[Person alloc] init]; person.age = 10; __weak Person *weakPerson = person; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (4.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"2-----age:%p",person); }); NSLog(@"1-----age:%p",weakPerson); }); NSLog(@"touchesBegan");}

输出结果:14:48:01.293818 0800 test[1199:403589] touchesBegan14:48:05.294127 0800 test[1199:403589] 1-----age:0x604000015eb014:48:08.582807 0800 test[1199:403589] 2-----age:0x604000015eb014:48:08.583129 0800 test[1199:403589] Person-dealloc

由来:gcd内部只要有强引用Person,Person就能够等待实施完再销毁!所以Person销毁时间为7秒。

-touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ Person *person = [[Person alloc] init]; person.age = 10; __weak Person *weakPerson = person; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (4.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"2-----age:%p",weakPerson); }); NSLog(@"1-----age:%p",person); }); NSLog(@"touchesBegan");}

输出结果14:52:29.036878 0800 test[1249:431302] touchesBegan14:52:33.417862 0800 test[1249:431302] 1-----age:0x6000000178d014:52:33.418178 0800 test[1249:431302] Person-dealloc14:52:36.418204 0800 test[1249:431302] 2-----age:0x0

由来:Person会等待强引用推行实现后放走,只要强援引试行完,就不会等待后推行的弱引用,会直接出狱的,所以Person释放时间为4秒。

不需要。

auto修饰变量,block不可能修改,因为block使用的时候是当中成立了变量来保存外界的变量的值,block只有改动内部和煦变量的权能,不能修改外界变量的权位。static修饰变量,block能够修改,因为block把外界static修饰变量的指针存入,block直接改变指针指向变量值,就能够修改外部变量值。全局变量值,全局变量无论哪个地方都得以修改,当然block内部也得以修改。

答案:编写翻译器会将__block变量包装成多个指标查看c 源码:

struct __Block_byref_age_0 { void *__isa;__Block_byref_age_0 *__forwarding;//age的地址 int __flags; int __size; int age;//age 的值};
  • __block能够用来减轻block内部不可能修改auto变量值的标题
  • __block无法修饰全局变量、静态变量
  • 编译器会将__block变量包装成叁个对象
  • __block修退换量:age->__forwarding->age
  • __Block_byref_age_0结构体内部地址和表面变量age是同一地点

图片 5__forwarding指针指向

NSMutableArray *arr = [NSMutableArray array];Block block = ^{ [arr addObject:@"123"]; [arr addObject:@"2345"];};

答案:能够,因为是addObject是利用NSMutableArray变量,并非由此指针改造NSMutableArray,假使是arr = nil,那正是改造了NSMutableArray变量,会报错。

当block在栈上时,并不会对__block变量发生强援用

block一旦未有开展copy操作,就不会在堆上block在堆上,程序猿就足以对block做内部存储器管理等操作,能够调节block的生命周期

  • 会调用block内部的copy函数
  • copy函数内部会调用_Block_object_assign函数
  • _Block_object_assign函数会对__block变量造成强援引
  • 对于__block 修饰的变量 assign函数对其强援引;对于外界对象 assign函数依据外界怎样引用而援用

图片 6block0复制到堆上图片 7block1复制到堆上

  • 会调用block内部的dispose函数
  • dispose函数内部会调用_Block_object_dispose函数
  • _Block_object_dispose函数会自动释放援用的__block变量

图片 8block被废弃图片 9block1被废弃

从四人置回应:

先看一段代码:

  • 一、block 知识回看

    block 是两个能够当做是一个代码块,用 ^{}打包起来的,类似于函数,要求用()小括号调用

    图片 10block的简约回看

  • 二 、block 的本色是如何?

    • block本质上也是一个OC对象,它里面也许有个isa指针
    • block是包装了函数调用以及函数调用环境的OC对象
    • block的底部结构如下图所示

      图片 11block的平底结构

  • 三 、block 的变量捕获

    • 2.1、为了保险block内部能够平常访问外部的变量,block有个变量捕获机制

      变量类型 能否捕获到block内部 访问方式
      局部变量 auto 能捕获到 值传递
      局部变量 static 能捕获到 指针传递
      全局部变量 不能捕获到 直接访问
    • 2.2、局地变量的走访

      图片 12block 的变量捕获图片 13block 的变量捕获

    • 2.3、全局地变量的拜候

      图片 14全部分变量是无力回天捕获的,也不须求捕获图片 15能够观察全局变量是心余力绌捕获的

    • 2.4、self的的主题材料

      图片 16self被捕获,JKName来自于self图片 17self的抓获难题

      • 提示:self是大家调用函数的时候,传进来的参数,self是一些变量,只要能捕获即是部分变量,反之,全局变量不能捕获。
  • 四、block的类型

    • 4.1、block有3种类型,能够透过调用class方法依然isa指针查看具体品种,最后都以继续自NSBlock类型

      • NSGlobalBlock ( _NSConcreteGlobalBlock )
      • NSStackBlock ( _NSConcreteStackBlock )
      • NSMallocBlock ( _NSConcreteMallocBlock )图片 18block有3系列型的打印图片 19应用程序的内部存款和储蓄器分配升迁:类对象也是寄存在在堆里面
    • 4.2、block在如何情状下的类型

    • 提醒:在测量检验上边包车型大巴品种从前请把ARC关掉

      图片 20ARC关掉图片 21block在如何景况下的连串图片 22block在有无Auto变量的探访

    • 4.3、各样档期的顺序的block调用copy后的结果如下所示(下面的 堆栈 感觉迷茫的看上面4.1对中对3类Block的存放位置)

      Block类型 副本源的配置存储域 复制效果
      NSStackBlock 从栈复制到堆
      NSGlobalBlock 程序的数据区域 还是原来的类型,什么也没做
      NSMallocBlock 引用计数器加1

    图片 23Block在copy后的打字与印刷

     int age = 10; void  = ^{ NSLog(@"age = %d",age); }; void  = ^{ NSLog; }; NSLog(@" nnblock1的类型是 %@ nnblock1 在copy后的类型是%@",[block1 class],[[block1 copy] class]); NSLog(@" nnblock2的类型是%@ nnblock2 在copy后的类型是%@",[block2 class],[[block2 copy] class]); NSLog(@" block2在两次copy后的类型是 n%@",[[[block2 copy] copy] class]); 下面是打印的结果 block1的类型是 __NSStackBlock__ block1 在copy后的类型是__NSMallocBlock__ block2的类型是__NSGlobalBlock__ block2 在copy后的类型是__NSGlobalBlock__ block2在两次copy后的类型是 __NSGlobalBlock__
    
    • 4.4、block的copy

      • 4.4.1、在ARC情况下,编写翻译器会依赖事态自行将栈上的block复制到堆上,比方以下境况
        • block作为函数再次回到值时
        • 将block赋值给__strong指针时
        • block作为Cocoa API中方法名含有usingBlock的方法参数时
        • block作为GCD API的点子参数时(GCD的Block都是被copy过的,会在block方法执行完后才会销毁
      • 4.4.2、MRC下block属性的提议写法
        • @property (copy, nonatomic) void ;
      • 4.4.3、ARC下block属性的提议写法
        • @property (strong, nonatomic) void ;
        • @property (copy, nonatomic) void ;
    • 4.5、 对象类型的auto变量

      • 当block里面访问了对象类型的auto变量时

        • 比如block是在栈上,将不会对auto变量爆发强援引

        • 一旦block被拷贝到堆上

          • 会调用block内部的copy函数
          • copy函数内部会调用_Block_object_assign函数
          • _Block_object_assign函数会按照auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,造成强援引可能弱援引
        • 假使block从堆上移除

          • 会调用block内部的dispose函数
          • dispose函数内部会调用_Block_object_dispose函数
          • _Block_object_dispose函数会自动释放援用的auto变量
          函数 调用时机
          copy 函数 栈上的Block复制到堆时
          dispose 函数 堆上的Block被废弃时
    • 4.6、 抛出多少个难点,讲出下边Person类几时释放

      • 第1种情况

        Person *person = [[Person alloc]init];person.age = 100;__weak Person *weakPerson = person;dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"age === %d",weakPerson.age); });
        
      • 第2种情况

        Person *person = [[Person alloc]init];person.age = 100;dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"age === %d", person.age); });
        
      • 第3种情况

        Person *person = [[Person alloc]init];person.age = 100;__weak Person *weakPerson = person;dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"age === %d", person.age); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"age === %d", weakPerson.age); }); });
        
      • 第4种情况

        Person *person = [[Person alloc]init];person.age = 100;__weak Person *weakPerson = person;dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"age === %d", weakPerson.age); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"age === %d",person.age); }); });
        
  • 五、__block修饰符

    • 5.1、__block能够用来缓和block内部不能修改auto变量值的问题

    • 5.2、__block不能够修饰全局变量、静态变量

    • 5.3、编写翻译器会将__block变量包装成八个对象

    • 5.4、看上边auto一时变量在抬高 __block后修改age的值

      __block int age = 10;void  = ^{ age = 20; NSLog(@"age = %d",age);};block();
      
    • 5.5、__block 可修改的法则

      图片 24age加上`__block`年轻成上面划线的结构体图片 25age结构体的项目图片 26结构体里面包车型客车针对

    • 5.6、__block的内部存款和储蓄器处理

      • 5.6.1、当block在栈上时,并不会对__block变量发生强援引
      • 5.6.2、当block被copy到堆时
        • 会调用block内部的copy函数
        • copy函数内部会调用_Block_object_assign函数
        • _Block_object_assign函数会对__block变量产生强引用
      • 5.6.3、当block从堆中移除时
        • 会调用block内部的dispose函数
        • dispose函数内部会调用_Block_object_dispose函数
        • _Block_object_dispose函数会自行释放援用的__block变量
      • 5.6.4、__block的__forwarding指针

        图片 27__block的__forwarding指针

      • 5.6.5、对象类型的auto变量、__block变量

        • 当block在栈上时,对它们都不会发出强援用
        • 当block拷贝到堆上时,都会经过copy函数来管理它们

          • __block变量

            _Block_object_assign&dst->a, src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
            

            目的类型的auto变量

            _Block_object_assign&dst->p, src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
            
        • 当block从堆上移除时,都会经过dispose函数来刑释解教它们

          • __block变量

            _Block_object_disposesrc->a, 8/*BLOCK_FIELD_IS_BYREF*/);
            

            指标类型的auto变量

            _Block_object_disposesrc->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
            
          对象 BLOCK_FIED_IS_OBJECT
          __block变量 BLOCK_FIED_IS_BYREF
      • 5.6.6、被__block修饰的对象类型
        • 当__block变量在栈上时,不会对针对的对象发生强引用
        • 当__block变量被copy到堆时
          • 会调用__block变量内部的copy函数
          • copy函数内部会调用_Block_object_assign函数
          • _Block_object_assign函数会依据所针对对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,产生强援引可能弱引用(注意:此间只限于ARC时会retain,MRC时不会retain
        • 如果__block变量从堆上移除
          • 会调用__block变量内部的dispose函数
          • dispose函数内部会调用_Block_object_dispose函数
          • _Block_object_dispose函数会自行释放指向的靶子
  • 六、block 的大循环引用难题

    • 6.1、先给我们显示贰个生生不息援引(巡回引用是大家常说的多少个类互相引用)

      图片 28在看一下Person的.h

      #import <Foundation/Foundation.h>typedef void ;@interface Person : NSObject@property(nonatomic,assign) int age;@property(nonatomic,copy) JKBlock block;@end
      

      能够见见地点图片的紫水晶色文字也提示了留存循环援引的标题,下边剖判一下,首先从Person的.h代码里面大家能够看来 person 强引用了block,那大家看看block是怎么是怎么强援用person的,大家生成一份C 代码看看

      图片 29blcok强指针指向person

    • 6.2、block 的大循环援引的消除(ARC下化解循环引用的题目) 提示:ARC与MRC的切换看上边4.2

      看6.第11中学间的代码,我们想只若是person强引用Block,能够每天调用block里面person属性的值,而block若引用pseron就好,那么了足以设置如下的代码改良

      Person *person = [[Person alloc]init];person.age = 100;__weak Person *weakPerson = person;person.block = ^{ NSLog(@"age ==== %d",weakPerson.age);};
      
      • __weak Person *weakPerson = person;__weak typeof weakPerson = person;同等, typeof是编译器的表征

        图片 30block若指针指向person

      • __unsafe_unretained Person *weakPerson = person;__unsafe_unretained typeof weakPerson = person;

      • __block Person *weakPerson = person;不可能不调用block,如下

        Person *person = [[Person alloc]init];person.age = 100;__block typeof weakPerson = person;person.block = ^{ NSLog(@"age ==== %d",weakPerson.age); weakPerson = nil;};person.block();
        

      图片 31weakPerson = nil打破循环引用

      • __weak__unsafe_unretained减轻循环援用的区分

        • __weak : 不会发出强引用,指向的靶子销毁时,会自行让指针置为nil
        • __unsafe_unretained : 不会发生强引用,不安全,指向的目的销毁时,指针存款和储蓄的地址值不变
    • 6.3、block 的巡回援用的化解(MRC下化解循环引用的标题), 提示:ARC与MRC的切换看上边4.2,MRC下是不帮衬若指针的

      • __unsafe_unretained Person *weakPerson = person;

        Person *person = [[Person alloc]init];person.age = 100;__unsafe_unretained typeof weakPerson = person;person.block = ^{ NSLog(@"age ==== %d",weakPerson.age); };[person release];
        
      • __block Person *weakPerson = person;

        Person *person = [[Person alloc]init];person.age = 100;__block typeof weakPerson = person;person.block = ^{ NSLog(@"age ==== %d",weakPerson.age); };[person release];
        

        看5.6.6、上面说的__block 在ARC时会retain,MRC时不会retain,也等于说不会在MRC下不会retain

        图片 32__block 在MRC下不会retain图片 33在MRC未有变异强援引

  • 七、block常问的多少个面试题

    • 7.1、block的原理是怎么着的?本质是怎样?

      答:封装了函数调用以致调用情形的OC对象

    • 7.2、__block的职能是怎么着?有如何使用注意点?

      答: __block能够用来缓和block内部不恐怕修改auto变量值的主题材料,__block不能够修饰全局变量、静态变量,编译器会将__block变量包装成叁个指标,注意的地点是:__block的内部存储器管理和在block所产生的结构体的OC对象在ARC时会retain,MRC时不会retain。

    • 7.3、block的天性修饰词为何是copy?使用block有哪些使用注意?答:block一旦未有打开copy操作,就不会在堆上(在堆上能够对其举办内部存款和储蓄器管理)使用注意:循环引用难题

    • 7.4、block在修改NSMutableArray,需无需增添__block?

      答:没有须求,看下边包车型地铁代码,另外__block能不加就不要加,因为会变卦贰个复杂的结构体

      NSMutableArray *array = [[NSMutableArray alloc]init];void  = ^{ [array addObject:@"1"];};Block();
      

下边通过代码验证那几个境况:

1.当block在栈上时,对它们都不会发生强援用
- blockModifyVariable { __block int a = 10; __block Person *person = [Person new]; person.name = @"mm"; BlockDemo block = ^{ a = 11; person = [Person new]; person.name = @"modified"; NSLog(@"%d---%@",a,person.name);//打印结果:11---modified }; block();}

block作为函数重返值和将block赋值给强指针:

2.当block拷贝到堆上时
  • 都会透过copy函数来拍卖它们
  • 对于__block 修饰的变量 assign函数对其强援引;对于外界对象 assign函数依照外界怎么着援用而援用__block变量
_Block_object_assign&dst->a, src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

对象类型的auto变量

_Block_object_assign&dst->p, src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
  • 都会透过dispose函数来刑满释放解除劳教它们__block变量
_Block_object_disposesrc->a, 8/*BLOCK_FIELD_IS_BYREF*/);

对象类型的auto变量

_Block_object_disposesrc->p, 3/*BLOCK_FIELD_IS_OBJECT*/);
  • 栈上__block的__forwarding指向小编
  • 栈上__block复制到堆上后,栈上block的__forwarding指向堆上的block,堆上block的__forwarding指向自家

图片 34__forwarding指向

分几上边回应:

下面是block通过clang转换成C 的代码 :

typedef void(^BlockDemo);- viewDidLoad { [super viewDidLoad]; int a = 1; int c = 2; // 访问自动变量 NSLog(@"%@",^{NSLog;});//打印结果:<__NSStackBlock__: 0x7ffeefac59e0> // block作为函数/方法返回值 NSLog(@"%@",[[self getBlock] class]); //打印结果:__NSMallocBlock__ BlockDemo strongBlock = ^{NSLog;}; // 强指针指向block NSLog(@"%@",[strongBlock class]); //打印结果:__NSMallocBlock__}- (BlockDemo)getBlock { int b = 1; return ^{ NSLog; };}
1.当__block变量在栈上时,不会对针对性的指标发生强援引
struct __ViewController__blockModifyVariable_block_impl_0 { struct __block_impl impl; struct __ViewController__blockModifyVariable_block_desc_0* Desc; __Block_byref_a_0 *a; // by ref __Block_byref_person_1 *person; // by ref __ViewController__blockModifyVariable_block_impl_0(void *fp, struct __ViewController__blockModifyVariable_block_desc_0 *desc, __Block_byref_a_0 *_a, __Block_byref_person_1 *_person, int flags=0) : a(_a->__forwarding), person(_person->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};

从上边代码的打字与印刷结果能够见见,单纯的二个做客auto变量的block是NSStackBlock类型,但当这样贰个block作为函数再次来到值,当有强指针指向那样的block时,它是NSMallocBlock类型。

2.当__block变量被copy到堆时
  • 会调用__block变量内部的copy函数
  • copy函数内部会调用_Block_object_assign函数
  • _Block_object_assign函数会依赖所针对对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强援用恐怕弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain)
  • MRC意况下不会依靠目的的修饰符援引,都以弱援引

加了__block修饰后,block结构体里面也是加多了八个分子变量,差异的是而不是直接破获外界的变量,而是扩大了七个__Block_byref开始的靶子。上边看那三个对象:

block作为GCD API的议程参数:

3.如果__block变量从堆上移除
  • 会调用__block变量内部的dispose函数
  • dispose函数内部会调用_Block_object_dispose函数_Block_object_dispose函数会自动释放指向的目的

两种方法:__weak、__unsafe_unretained、__block

struct __Block_byref_a_0 { void *__isa;__Block_byref_a_0 *__forwarding; int __flags; int __size; int a;};struct __Block_byref_person_1 { void *__isa;__Block_byref_person_1 *__forwarding; int __flags; int __size; void (*__Block_byref_id_object_copy)(void*, void*); void (*__Block_byref_id_object_dispose); Person *__strong person;};static void __Block_byref_id_object_copy_131(void *dst, void *src) { _Block_object_assigndst   40, * src   40), 131);}static void __Block_byref_id_object_dispose_131(void *src) { _Block_object_dispose(* src   40), 131);}
- GCDBlcokCaptureObject { Person *person = nil; person = [Person new]; person.name = @"jacden"; person.friends = @[@"s",@"2"]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"----name:%@-----",person.name); NSLog(@"------dispatch_after-------");});## 情况1和情况2分别和上面代码一起执行## 情况1person = [Person new];person.name = @"lily";打印结果:/* person dealloc----name:jacden-----------dispatch_after------- person dealloc*/## 情况2person.name = @"lily";打印结果:/*----name:lily-----------dispatch_after------- person dealloc*/}
1.第一种艺术:__weak
Person *person = [[Person alloc] init];// __weak Person *weakPerson = person;__weak typeof weakPerson = person;person.block = ^{ NSLog(@"age is %d", weakPerson.age);};

对照四个结构体,对象类型的person内部多了copy和dispose那多少个管理内存的函数。对象内部分别有和表面变量名称一样的a和person。何况能看见__Block_byref_person_1是强引用着person的。__Block_byref_person_1是还是不是强援用person要在于指向外界的person变量是用什么样修饰符修饰,要是是用weak大概__unsafe_unretained,那这里正是弱援用。

解释下方面包车型客车打字与印刷结果,首先1s后block块的代码能进行表明这些block在堆上不是在栈上,能打字与印刷person的name值表达block内部强援引了person对象,不然GCDBlcokCaptureObject方法执行完后person、block就能够销毁,就能再有打字与印刷。

2.次之种艺术:__unsafe_unretained
__unsafe_unretained Person *person = [[Person alloc] init];person.block = ^{ NSLog(@"age is %d", weakPerson.age);};

上面是方法blockModifyVariable调换后的代码:

意况1先打字与印刷person dealloc,那是因为dispatch_after是异步实行的,1s后才会实行它block内部代码,情形1的代码会先试行,情状1代码试行完方法GCDBlcokCaptureObject也就实行完成了,由此情状1person指南针销毁,新创造的person对象未有强指针指向它,于是销毁调用了dealloc方法。name打字与印刷结果为jacden, 是因为block捕获auto变量跟外界变量的类型是同等的,此时外界是Person *指南针,block内部会有个Person *指南针指向外界变量,情形1是修改了表面person的针对性,而block内部的Person *指南针仍指向原本的变量。全数打字与印刷结果依然依旧是jacden;1s后block推行完结,block销毁,不再有指针指向创造的第一个person对象,该指标销毁调用dealloc方法。

3.第二种方法:__block
__block Person *person = [[Person alloc] init];person.block = ^{ NSLog(@"age is %d", person.age); person = nil;};person.block();
static void _I_ViewController_blockModifyVariable(ViewController * self, SEL _cmd) { __attribute__((__blocks__ __Block_byref_a_0 a ={ 0, (__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10 }; __attribute__((__blocks__ __Block_byref_person_1 person = { 0, (__Block_byref_person_1 *)&person, 33554432, sizeof(__Block_byref_person_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((Person *objc_msgSend)objc_getClass, sel_registerName }; (id, SEL, NSString * _Nonnull))objc_msgSend)(person.__forwarding->person), sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_zx_b2snvmns3v14tgx8jk3ysvrm0000gn_T_ViewController_5067e2_mi_0); BlockDemo block = &__ViewController__blockModifyVariable_block_impl_0__ViewController__blockModifyVariable_block_func_0, &__ViewController__blockModifyVariable_block_desc_0_DATA, (__Block_byref_a_0 *)&a, (__Block_byref_person_1 *)&person, 570425344)); (__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);}

动静2只是修改了外部person指针指向的对象的属性值,即所针对的内部存款和储蓄器中的对象产生了更改,指针指向的地址跟block内部针对的地点是大同小异的,全数打字与印刷结果是时尚值;1s后block试行实现,block销毁,不再有指针指向制造的率先个person对象,该指标销毁调用dealloc方法。

4.二种方式相比
  • __weak:不会时有产生强援用,指向的对象销毁时,会自动让指针置为nil
  • __unsafe_unretained:不会生出强引用,不安全,指向的目的销毁时,指针存款和储蓄的地点值不改变
  • __block:必得把援用对象置位nil,而且要调用该block

图片 35__weak 和 __unsafe_unretained 化解循环援用格局图片 36__block消除循环援用格局

两种方法:__unsafe_unretained、__block

能够见到用__block修饰后将变量a和person包装成__Block_byref_a_0和__Block_byref_person_1。block初始化时将那多少个目的赋值给了block内部的分子变量a和person。

对此情状2,有个吸引的点,修改person对象的属性值和改造person那些指针是不一样等的,须要卓越通晓下。

1.首先种方法:__unsafe_unretained
__unsafe_unretained Person *person = [[Person alloc] init];person.block = ^{ NSLog(@"age is %d", weakPerson.age);};

block的积极分子变量desc:

上边我们来看下block内部访问对象类型的auto变量的一对状态;

2.次之种方法:__block
__block Person *person = [[Person alloc] init];person.block = ^{ NSLog(@"age is %d", person.age);};
static struct __ViewController__blockModifyVariable_block_desc_0 { size_t reserved; size_t Block_size; void (struct __ViewController__blockModifyVariable_block_impl_0*, struct __ViewController__blockModifyVariable_block_impl_0*); void (struct __ViewController__blockModifyVariable_block_impl_0*);} __ViewController__blockModifyVariable_block_desc_0_DATA ={ 0, sizeof(struct __ViewController__blockModifyVariable_block_impl_0), __ViewController__blockModifyVariable_block_copy_0, __ViewController__blockModifyVariable_block_dispose_0};static void __ViewController__blockModifyVariable_block_copy_0(struct __ViewController__blockModifyVariable_block_impl_0*dst, struct __ViewController__blockModifyVariable_block_impl_0*src) {_Block_object_assign&dst->a, src->a, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign&dst->person, src->person, 8/*BLOCK_FIELD_IS_BYREF*/);}static void __ViewController__blockModifyVariable_block_dispose_0(struct __ViewController__blockModifyVariable_block_impl_0*src) {_Block_object_disposesrc->a, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_disposesrc->person, 8/*BLOCK_FIELD_IS_BYREF*/);}
  • block在栈上,不会对auto变量发生强援用
  • block被拷贝到堆上,内部会调用copy函数,copy函数会调用_Block_object_assign函数,_Block_object_assign函数依据auto变量的修饰符(__strong、__weak、__unsafe_unretained)等作出相应的操作,产生对auto变量的强引用或弱援引
  • 当block从堆上移除时,调用block内部dispose函数,dispose函数内部调用_Block_object_dispose函数自动释放引用的auto变量;

desc中用copy函数和dispose函数来保管包装后的指标a和person的内存;

函数 调用时机
copy函数 栈上的block复制到堆时
dispose函数 堆上的block被废弃时

下边看看block实行代码块时是怎么修改和取值的:

访谈变量用strong修饰时:

static void __ViewController__blockModifyVariable_block_func_0(struct __ViewController__blockModifyVariable_block_impl_0 *__cself) { __Block_byref_a_0 *a = __cself->a; // bound by ref 取出block内部的成员变量a __Block_byref_person_1 *person = __cself->person; // bound by ref 取出block内部的成员变量person (a->__forwarding->a) = 11; // __forwarding指针:当block在栈上,__forwarding指向栈上block的成员变量a,当block被拷贝到堆上__forwarding指向拷贝到堆上的block的成员变量a,保证block内部一定是访问到堆上的变量。这一步就是通过__forwarding指针找到变量a对其修改。 // 通过__forwarding指针找到变量person对其修改。 (person->__forwarding->person) = ((Person *objc_msgSend)objc_getClass, sel_registerName; (id, SEL, NSString * _Nonnull))objc_msgSend)(person->__forwarding->person), sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_zx_b2snvmns3v14tgx8jk3ysvrm0000gn_T_ViewController_5067e2_mi_1); // 取值时也是通过__forwarding指针找到变量a、person。 NSLog((NSString *)&__NSConstantStringImpl__var_folders_zx_b2snvmns3v14tgx8jk3ysvrm0000gn_T_ViewController_5067e2_mi_2,(a->__forwarding->a),((NSString *objc_msgSend)(person->__forwarding->person), sel_registerName;}
- blockCaptureObject { Person *person = [Person new]; person.name = @"jack"; BlockDemo block = ^{ NSLog(@"%@",person.name); }; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ block;}下面是通过clang转换成c  后的代码struct __ViewController__blockCaptureObject_block_impl_0 { struct __block_impl impl; struct __ViewController__blockCaptureObject_block_desc_0* Desc; Person *__strong person; ##这里显示是强指针 __ViewController__blockCaptureObject_block_impl_0(void *fp, struct __ViewController__blockCaptureObject_block_desc_0 *desc, Person *__strong _person, int flags=0) : person { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __ViewController__blockCaptureObject_block_func_0(struct __ViewController__blockCaptureObject_block_impl_0 *__cself) { Person *__strong person = __cself->person; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_zx_b2snvmns3v14tgx8jk3ysvrm0000gn_T_ViewController_a592c8_mi_1,((NSString *objc_msgSend)person, sel_registerName;}static void __ViewController__blockCaptureObject_block_copy_0(struct __ViewController__blockCaptureObject_block_impl_0*dst, struct __ViewController__blockCaptureObject_block_impl_0*src) {_Block_object_assign&dst->person, src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);}static void __ViewController__blockCaptureObject_block_dispose_0(struct __ViewController__blockCaptureObject_block_impl_0*src) {_Block_object_disposesrc->person, 3/*BLOCK_FIELD_IS_OBJECT*/);}static struct __ViewController__blockCaptureObject_block_desc_0 { size_t reserved; size_t Block_size; void (struct __ViewController__blockCaptureObject_block_impl_0*, struct __ViewController__blockCaptureObject_block_impl_0*); void (struct __ViewController__blockCaptureObject_block_impl_0*);} __ViewController__blockCaptureObject_block_desc_0_DATA = { 0, sizeof(struct __ViewController__blockCaptureObject_block_impl_0), __ViewController__blockCaptureObject_block_copy_0, __ViewController__blockCaptureObject_block_dispose_0};static void _I_ViewController_blockCaptureObject(ViewController * self, SEL _cmd) { Person *person = ((Person *objc_msgSend)objc_getClass, sel_registerName; (id, SEL, NSString * _Nonnull))objc_msgSend)person, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_zx_b2snvmns3v14tgx8jk3ysvrm0000gn_T_ViewController_a592c8_mi_0); BlockDemo block = &__ViewController__blockCaptureObject_block_impl_0__ViewController__blockCaptureObject_block_func_0, &__ViewController__blockCaptureObject_block_desc_0_DATA, person, 570425344)); dispatch_after(dispatch_time, (2 * 1000000000ull)), dispatch_get_main_queue(), &__ViewController__blockCaptureObject_block_impl_1__ViewController__blockCaptureObject_block_func_1, &__ViewController__blockCaptureObject_block_desc_1_DATA, block, 570425344)));}

代码中关于__forwarding指针指向的求证可参照他事他说加以考察下图:

打字与印刷结果:jackperson dealloc

图片 37forwarding.png

从代码能够见到block内部会有个Person *的强指针指向外界的person,__ViewController__blockCaptureObject_block_desc_0函数相比较访谈基本数据类型,多了三个成员变量copy函数和dispose函数,在函数的开首化时将__ViewController__blockCaptureObject_block_copy_0函数赋值给了copy函数,将__ViewController__blockCaptureObject_block_dispose_0赋值给了dispose函数。这多少个函数就是上边所列举的用来管理person的内部存款和储蓄器。别的2s后进行block块代码,能打字与印刷出来name的值表达block内部强引用着person。block实行完,person实践dealloc方法,表达block不再有强引用着person。

对上述做个小结:用__block修饰auto变量时,编写翻译器会将__block变量包装成靶子,对象富含该auto变量且有个__forwarding指针指向包装后的对象,而block内部会具有那个目的;在block内部访谈auto变量实际上是经过获取block内部有着的卷入后的靶子,然后经过这些指标中的__forwarding指针找到包装后的指标或卷入后被复制到堆上的靶子,最终收取对象中包罗的变量进行取值或修改;那正是干什么用__block修饰之后可以修退换量的原故。

做客变量用weak修饰时:

当block在栈上时,不会对__block变量发生强援引。当block被拷贝到堆上时会调用block内部的copy函数,copy函数调用内部的_Block_object_assign函数根据所指向对象的修饰符对__block变量形成强援引或弱援用(注意:ARC会retain,MRC时不会retain);block被拷贝到堆上后,其内部用到的积极分子也都会被拷贝到堆上。当多少个栈上的block内部访谈同一个block变量,五个block被拷贝到堆上后,堆上唯有会有一份__block变量的正片,那五个block仍同偶尔候兼有这一个变量;仿照效法下图:

- blockCaptureObject { Person *person = [Person new]; person.name = @"jack"; __weak Person *weakPerson = person; BlockDemo block = ^{ NSLog(@"%@",weakPerson.name);// 打印结果:null }; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ block;}下面是通过clang转换成c  后的代码struct __ViewController__blockCaptureObject_block_impl_0 { struct __block_impl impl; struct __ViewController__blockCaptureObject_block_desc_0* Desc; Person *__weak weakPerson; ##这里显示是弱指针 __ViewController__blockCaptureObject_block_impl_0(void *fp, struct __ViewController__blockCaptureObject_block_desc_0 *desc, Person *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __ViewController__blockCaptureObject_block_func_0(struct __ViewController__blockCaptureObject_block_impl_0 *__cself) { Person *__weak weakPerson = __cself->weakPerson; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_zx_b2snvmns3v14tgx8jk3ysvrm0000gn_T_ViewController_b47619_mi_1,((NSString *objc_msgSend)weakPerson, sel_registerName; }static void __ViewController__blockCaptureObject_block_copy_0(struct __ViewController__blockCaptureObject_block_impl_0*dst, struct __ViewController__blockCaptureObject_block_impl_0*src) {_Block_object_assign&dst->weakPerson, src->weakPerson, 3/*BLOCK_FIELD_IS_OBJECT*/);}static void __ViewController__blockCaptureObject_block_dispose_0(struct __ViewController__blockCaptureObject_block_impl_0*src) {_Block_object_disposesrc->weakPerson, 3/*BLOCK_FIELD_IS_OBJECT*/);}static struct __ViewController__blockCaptureObject_block_desc_0 { size_t reserved; size_t Block_size; void (struct __ViewController__blockCaptureObject_block_impl_0*, struct __ViewController__blockCaptureObject_block_impl_0*); void (struct __ViewController__blockCaptureObject_block_impl_0*);} __ViewController__blockCaptureObject_block_desc_0_DATA = { 0, sizeof(struct __ViewController__blockCaptureObject_block_impl_0), __ViewController__blockCaptureObject_block_copy_0, __ViewController__blockCaptureObject_block_dispose_0};static void _I_ViewController_blockCaptureObject(ViewController * self, SEL _cmd) { Person *person = ((Person *objc_msgSend)objc_getClass, sel_registerName; (id, SEL, NSString * _Nonnull))objc_msgSend)person, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_zx_b2snvmns3v14tgx8jk3ysvrm0000gn_T_ViewController_b47619_mi_0); __attribute__((objc_ownership Person *weakPerson = person; BlockDemo block = &__ViewController__blockCaptureObject_block_impl_0__ViewController__blockCaptureObject_block_func_0, &__ViewController__blockCaptureObject_block_desc_0_DATA, weakPerson, 570425344)); dispatch_after(dispatch_time, (2 * 1000000000ull)), dispatch_get_main_queue(), &__ViewController__blockCaptureObject_block_impl_1__ViewController__blockCaptureObject_block_func_1, &__ViewController__blockCaptureObject_block_desc_1_DATA, block, 570425344)));}

图片 38__block_copy.png

打印结果:person dealloc

通过代码也表达下:

从代码能够见见block会有个Person *的弱指针指向外界的person,同样__ViewController__blockCaptureObject_block_desc_0函数相比较访谈基本数据类型,多了五个成员变量copy函数和dispose函数。其余实行block块代码前,person就调用了dealloc方法,打字与印刷name的值为,表明block内部从不强援引着person,blockCaptureObject方法实行完person就销毁了。

- modifyVariable { __block int a = 10; __block Person *person = [Person new]; person.name = @"mm"; BlockDemo block = ^{ a = 11; NSLog(@"%d---%@",a,person.name); }; BlockDemo block1 = ^{ a = 12; NSLog; }; struct __ViewController__modifyVariable_block_impl_0* blockImpl = (__bridge struct __ViewController__modifyVariable_block_impl_0*)block; struct __ViewController__modifyVariable_block_impl_0* blockImpl1 = (__bridge struct __ViewController__modifyVariable_block_impl_0*)block1; NSLog(@"%p -- %p",blockImpl->a,blockImpl1->a);// 打印结果:0x600000438600 -- 0x600000438600 block(); block1();}// 以下是block转换成c  后的代码,我们通过struct __ViewController__modifyVariable_block_desc_0 { size_t reserved; size_t Block_size; void ; void ;};struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr;};struct __Block_byref_a_0 { void *__isa; struct __Block_byref_a_0 *__forwarding; int __flags; int __size; int a;};struct __ViewController__modifyVariable_block_impl_0 { struct __block_impl impl; struct __ViewController__modifyVariable_block_desc_0* Desc; struct __Block_byref_a_0 *a; // by ref };

__ViewController__modifyVariable_block_impl_0是block的尾巴部分达成,我们经过__ViewController__modifyVariable_block_impl_0来访谈block内部装有的__block变量a,代码中得以见到那八个block内部有着的a的内部存款和储蓄器地址同样的。

当block从堆中移除时,会调用block内部的dispose函数,dispose函数内部调用_Block_object_dispose函数,_Block_object_dispose函数自动释放__block变量。进度如下图:

图片 39dispose.png

本文由星彩网app下载发布于计算机编程,转载请注明出处:OC底层知识,Block访谈对象类型

TAG标签: 星彩网app下载
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。