本文主要内容
1.影响目标内存的要素
2.目标的内存散布
3.位域和联合体
4.nonPointerisa
5.怎么利用isa的位运算得到类目标
6.new办法
一、研讨影响目标内存的要素
创立工程"01_影响目标内存的要素"
,新建HGPerson
类,给类增加一些特点和办法(有实现),如下图
在main.m
中创立类目标,并给其特点进行赋值,经过手动核算而且打印目标需求的内存巨细(isa + 成员变量的值
)和体系实践分配的内存巨细别离为40字节、48字节,发现和上一篇中定论一致:目标内部需求内存为8字节对齐,体系实践分配的内存为16字节对齐!
阐明:
目标需求的内存巨细核算:isa + 成员变量的值=8+8+8+4+8+2=40(38,8字节对齐)
体系实践分配的内存巨细核算:16字节对齐,所认为48。
一起还得出一个定论:体系实践分配给目标的内存巨细不仅和类目标中增加的办法无关,一起也与成员变量(特点)的次序无关
。
所以影响目标内存的要素只要目标的成员变量的值
,与其他要素无关!
弥补内容:影响目标内存要素的特殊情况
如下图,自定寄父类和子类,父类中特点count在1号方位和2号方位时,体系对目标的成员变量次序不做重排!因而在1号方位时目标需求内存和体系分配的内存别离为72和80,2号方位时目标需求内存和体系分配的内存均为64
。
呈现以上问题的原因
:当子类承继父类的数据结构时,父类是一块接连的内存空间,子类无法修改父类的数据结构,因而此例中”HGHuman”中count的方位不同对子类的目标的内存巨细产生了影响。
⚠️留意
:在本例中替换不同的方位,内存巨细变化为16个字节(体系分配),关于内存的耗费相对很小,在开发过程中几乎能够疏忽!
二、目标的内存散布
那么体系分配给目标的内存中,数据是怎么排列的?其间的特点的内存散布会依照声明或许赋值时的次序排列吗?如下图,经过LLDB调试,x/6gx p打印目标p的内存(x/6gx
详见汇编指令和LLDB常用命令汇总中的x/nuf
阐明)。
留意:观察内存散布,发现特点age、number所占内存共6字节,会被体系放到同一个8字节中,达到内存优化的意图!
由上图调试得出定论:目标的内存散布与特点声明或赋值次序无关,苹果体系会主动重排目标的特点的次序,达到内存优化的意图!
三、位域和联合体
1.位域
写法如下图中,”:”后边表明”位数”,1个字节8位,其规模由前面数据类型决议。假如数据类型为int,则甚至能够写成”32″。
留意:
- 后边位域的长度不能超过数据类型最大长度。如图中”a:”后边的数字不能超过7
- 1个位域有必要存储在一个位域中,超过则存放到下一个字节。如图中“a:”后边为7,则b有必要存储到下一字节,所以图中
struct3
的巨细为4个字节。特别留意
:”struct4″的巨细还需遵循”结构体对齐规矩”(见iOS底层原理之目标的底层探求(上))- 此办法用于节约内存,但因为节约内存有限,只要在体系级别才会运用,日常开发中很少运用。
2.联合体
联合体写法类似结构体,仅仅把”struct
“换成”union
“。经过打印结构体和联
会拓荒满足的内存给结构体,结构体会依次给对应成员变量赋值,可一起有值。而体系给联合体拓荒满足打的内存,赋值时,其间成员变量互斥,只能给某一个成员变量赋值。如下图:
打印联合体内成员变量的内存发现是同一块内存
。
联合体内存巨细核算规矩
:
1.有必要能够容纳最大的成员变量;
2.经过"1."核算出来的巨细有必要是其最大成员变量(根本数据类型)的整数倍。
结构体HGTeacher2
内存巨细为8,联合体HGTeacher3
内存巨细为8(a为7,int为4,根据规矩为8),联合体HGTeacher4
内存巨细为5(根本数据类型char是1字节)。
总结
结构体和联合体的区别
- 两者内存巨细的核算规矩不同;
- 结构体成员变量能够共存,内存拓荒粗豪;
- 联合体成员变量互斥,能够必定的节约内存空间。
四、引出isa和nonPointerisa
回忆iOS底层原理之目标的底层探求(上),盯梢objc源码到“核算目标所需内存的巨细instanceSize
函数和体系实践分配给目标的内存巨细calloc(1, size)
的方位。经过剖析,由下图可知,此刻(断点1之前)分配给目标的内存并未和类绑定,而履行initInstanceIsa
函数(或initIsa
函数),即断点2后,经过LLDB调试发现,目标obj
现已和LGPerson
类绑定。因而需求研讨initInstanceIsa
函数(或initIsa
函数)内部即函数objc_object::initIsa
的工作。
进入函数objc_object::initIsa
发现,创立了一个isa_t
的isa,isa_t
是一个联合体。
此刻引出nonPointerIsa
,内存优化的手法。Isa指针是一个class类型的结构体指针,主要用来存储内存地址。Isa指针有8个字节即64位,存储类目标的内存地址无需64位空间,每个目标都存在Isa指针会导致内存的糟蹋,为了减少糟蹋,体系将与目标休戚相关的信息存在此空间(64位),如下图中的引证计数extra_rc
,此刻就用到nonPointerIsa
。【此处忽然引出时有疑问:忽然就引出nonPointerIsa,没反应过来,其实便是剖析了Isa指针后需求进行内存优化,所以就引出!
】
留意:
1.isTaggedPointer也是内存优化手法。
2.”newisa.extra_rc = 1;“意为:目标引证计数的值,即经过alloc创立出来的目标引证计数即为1.
前面提到isa_t
是一个联合体。曾经版本均为Class
类型的结构体指针,而当时遍及运用nonPointerIsa
,为了兼容性所以isa_t
选用联合体类型。
五、怎么利用isa的位运算得到类目标
基于的当时都是nonPointerIsa
,isa指针存储的内容保存到了名为ISA_BITFIELD
(上图中)的结构体中。跳转到此结构体中,因为本人运用的intel
64位处理器,所以运用如下图中的结构体内容:
ISA_BITFIELD结构体解析
:
- 低位存储
nonpointer
、has_assoc
、has_cxx_dtor
;shiftcls
;- 高位存储
magic
、weakly_referenced
、unused
、has_sidetable_rc
、extra_rc
.
每一“位”具体代表阐明如下:
nonPotinter_isa
1.nonpointer:表明是否对isa指针敞开指针优化,0为纯isa指针,1为不止是类目标地址,isa指针中包含了类信息、目标的引证计数等;
2.has_assoc:关联目标标志位,0没有,1存在;
3.has_cxx_dtor:该目标是否有C++或许Objc的析构器,假如有析构函数,则需求做析构逻辑,假如没有,则能够更快的开释目标;
4.shiftcls:存储类指针的值,敞开指针优化的情况下,在intel 64架构中由44位用来存储类指针;
5.magic:用于调试器判断当时目标是真的目标还是没有初始化的空间;
6.weakly_referenced:标志目标是否被指向或许曾经指向一个ARC的弱变量,没有弱引证的目标能够更快开释;
7.unused:标志未被运用的空间【自己理解】
8.has_sidetable_rc:当目标引证计数大于extra_rc所能存储的最大规模时,则需求借用该变量存储进位;
9.extra_rc:表明该目标的引证计数值,当目标的引证计数超出最大的存储规模时,则需求运用到上面的has_sidetable_rc。
剖析HGPerson的实例目标p,运用如下办法,获取并验证类目标的内存地址:
在intel 64架构(x86_64)中,将isa指针内存地址右移3位(清除低3位),左移20位(清除高17位),再右移17位,得到的内存地址即为类目标的内存地址。
实操如下图:
模拟器调试:
真机调试:
弥补内容:
1.验证引证计数的值
经过内存地址的改换,也可获取到目标引证计数的值,即最高1位的值(真机):将isa指针地址右移45位(1+1+1+33+6+1+1+1),得到引证计数为2;
留意⚠️:模拟器验证有问题,待解决!!!
经过探求发现:运用isa & ISA_MASK(对应架构)即可得到类目标的内存地址!
模拟器:
真机:
2.无法打断点的问题解决
在调试过程中发现,在某处打了断点后,运行工程,在打了断点的方位不会中止,而且断点方位显现为虚线的。呈现这个问题或许是因为体系优化导致的,解决办法
:找到"Build Setting"
搜索lto
,设置“Link-Time Optimization”
为“No”一般都可解决。假如不能解决,或许是其他原因导致的,因情况而定。
六、new办法
探求new
办法的实质,首要创立工程”06_new办法”,在类HGPerson,重写init
办法,如下图。运用alloc-init和new别离创立目标p、p1,打印两者特点发现均已被赋值。
常识扩展:工厂设计形式
运用对工厂的承继然后创造出其他的子工厂,在其子工厂进行创立类的目标,后续直接调用该工厂的那个重写办法即可。如是说,NSObject中有init办法,即为工厂形式,其子类在创立时,即可用此办法进行特点等的初始化,得出不同的目标,但父类NSObject自身init没有做事情。
盯梢汇编及源码
new
的办法源码如下:
new
办法实践调用了callAlloc
和init
办法,callAlloc
实践又调用了alloc
,所以可看做new
和[[alloc] init]
意义相同。盯梢汇编发现,先调用了objc_opt_new
函数,找到对应源码同new
办法同理:
本文总结
- 探求目标内存散布
- 探求isa指针本质
- 了解new办法
有任何问题,欢迎各位谈论指出!觉得博主写的还不错的麻烦点个赞喽