iOS中内存管理机制是开发中一项很重要的知识,了解iOS中内存管理的规则不管是在开发中还是在学习中都能很大程度的帮助我们提升效率。下面我就根据自己的理解,详细梳理一下内存管理相关的知识。
在说内存管理之前,我们首先要了解什么内存。一块内存条,是一个从下至上地址依次递增的结构,内存条中主要分为几大类:栈区(stack)、堆区(heap)、常量区、代码区(.text)、保留区。常量区分为未初始化区域(.bss)和已初始化区域(.data),栈区stack存储顺序是由高地址存向低地址,而堆区是由低地址向高地址存储。内存条中地址由低到高的区域分别为:保留区,代码区,已初始化区(.data),未初始化区(.bss),堆区(heap),栈区(stack),内核区。而程序员操作的主要是栈区与堆区还有常量区。
关于iOS内存管理的方案其实并非只有散列表一种,还有一种更为高效且为内存高效节省空间的方法叫做TaggedPointer,表明加标记的指针,我们可以理解为在是指针内部增加一些特殊的信息。
那么为什么要使用taggedPointer这种内存管理方法呢,其如何达到节省内存的目的呢。举个例子,比如在OC中一个NSNumber对象,在32位中的系统中占用4个字节的空间,但是迁移至64位系统中后,其占用空间达到了8字节,以此类推,所有在64位系统中占用空间会翻倍的对象,在迁移后会导致系统内存剧增,即时他们根本用不到这么多的空间,所以苹果对于一些小型数据,采用了taggedPointer这种方式管理内存。
其主要的原理就是在对象的指针中加入特定需要记录的信息,以及对象所对应的值,在64位的系统中,一个指针所占用的内存空间为8个字节,已足以存下一些小型的数据量了,当对象指针的空间中存满后,再对指针所指向的内存区域进行存储,这就是taggedPointer。距离NSNumber,最低4位用于标记是什么类型的数据(long为3,float则为4,Int为2,double为5),而最高4位的“b”表示是NSNumber类型;其余56位则用来存储数值本身内容。
之前runtime文章中有提到过objc_objcet对象中isa指针分为指针型isa与非指针型isa(NONPOINTER_ISA),运用的便是类似这种技术。下面详细解读一下NONPOINTER_ISA:
在一个64位的指针内存中,第0位存储的是indexed标识符,它代表一个指针是否为NONPOINTER型,0代表不是,1代表是。第1位has_assoc,顾名思义,1代表其指向的实例变量含有关联对象,0则为否。第2位为has_cxx_dtor,表明该对象是否包含C++相关的内容或者该对象是否使用ARC来管理内存,如果含有C++相关内容或者使用了ARC来管理对象,这一块都表示为YES,第3-35位shiftcls存储的就是这个指针的地址。第42位为weak