前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Objective C之Block浅谈

Objective C之Block浅谈

原创
作者头像
SheltonWan
修改2019-06-14 10:53:20
5120
修改2019-06-14 10:53:20
举报

编译器在遇到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修饰符,其影响的只是对象引用计数。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档