Objective C之Block浅谈

编译器在遇到Block的时候,

如果是不带有__block修饰符的外部局部变量会进行变量拷贝,不支持在Block内部修改外部局部变量。

1、对于基本数据类型,譬如int,float,struct,相当于在Block内部创建一份新的拷贝,虽然在Block中变量名字一样,实质上已经是不同的变量,修改外部变量不会影响到Block中的同名变量。

2、对于Obj C对象变量,Block会将对象浅拷贝,也就是拷贝对象指针,可以理解为新建一个指针,该指针与外部同名指针指向同一个内存块。通过NSLog(@“%p”,obj),可以输出相同的内存地址。通过NSLog(@“%p”,&obj),可以输出不同的内存地址。修改obj的属性会影响到Block中同名obj的属性,因为他们共享相同对象。但是如果对外部对象直接赋值,外部对象和Block中同名对象就指向不同的内存块了,这时候再修改外部对象的属性就不会改变Block同名对象了。

如果是带有 __block修饰符的外部变量,编译器会将变量转移到堆去,不存在对象或数据类型的多份内存拷贝。

1、对于基本数据类型,会把变量移到堆内存中存放,在Block代码之后,Block和外部这时候都是引用堆内存中同一个变量了,修改Block和外部同名变量实质是修改同一个变量了,会相互影响。如果查看基本数据类型的地址,会发现编译器在处理Block的时候修改了其地址,将变量从栈中删除,转移到堆内存去了。

2、对于Obj C对象,跟基本数据类型一样,会把变量移到堆去,在Block代码之后,Block和外部这时候都是引用堆内存中同一个变量了,不存在多份内存副本。如果在Block外部将一个新的对象B赋值给带__block修饰符的对象A,会导致__block对象A立即销毁,然后将新对象B浅拷贝到堆原来的地址上,也就是存放该对象A指针的地址不变,只是对象A指向了新的地方去了而已(和B指向同一块内存了)。新对象B依然在栈中,__block对象A依然在原来堆地址中。

所有定义为__block类型的变量,在编译时都会变为一个个block对象变量。在编译时系统会为每个带__block修饰的变量生成一个和OC类内存结构兼容的结构体。

通过查看变量地址,可以发现,在block代码前后的__block修饰的局部变量地址会发生改变,之前是存放在栈中,后来存放到堆去了。在ARC模式中,对于不带有__block修饰符的对象,Block会自动增加该对象的引用计数,而带有__block修饰符的局部对象,对象移到堆去,没有增加引用计数,这也是为什么在block外给带有__block修饰符的局部变量赋值,有可能会导致原来对象dealloc函数立即触发。

当定义__block修饰的变量时,系统会把他转化为一个OC对象。 为什么要把__block定义的变量转变为OC对象呢?这个是和__block这个关键字所表达的意思是一致的,也就是定义为__block类型的变量是不会在block代码块内产生副本的,而是保持唯一性。

代码中定义的block块,被拆分为了block对象和全局函数两部分来实现。因此可以看出在iOS内所有定义的block代码块系统在编译时都会转化为个OC对象(NSBlock类是用来描述block代码块的OC类,系统一共支持栈block:NSStackBlock,堆block:NSMallocBlock,全局block:NSGlobalBlock三种类型的block。具体的细节和差异不在本文展开,请大家自行查找相关的资料。)。因此在编译时我们会为每个block代码块都生成一个和OC类兼容的结构体。

一旦在代码中出现了block代码块,编译时就会建立一个block对象,然后将block对象关联的函数代码地址、以及使用的外面的数据作为block对象的构造函数的参数来创建这个block对象。

对于__strong和__weak修饰符,其影响的只是对象引用计数。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券