天天看点

《编写高质量代码:改善Objective-C程序的61个建议》——建议10:在64位环境下尽可能利用标记指针

本节书摘来自华章出版社《编写高质量代码:改善objective-c程序的61个建议》一 书中的第2章,作者:刘一道,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

在mac os x 10.6(雪豹)中开始支持64位,如今最新版本iphone 5s也开始采用arm64架构。在64位化的过程中,其中一个比较关键的改进就是,mac os 10.7(美洲虎)和ios 7的64位环境先后引入了标记(tagged)指针。

下面就简单地来介绍一下标记(tagged)指针。在介绍标记(tagged)指针之前有必要介绍一下指针地址对齐概念和64位环境的一些变化。

指针地址对齐

在32位环境下,要读取一个32位整数,如果这个32位整数在内存地址为0x00000002-0x00000006(仅作举例,这个地址一般是被系统保留的)的内存上,读取这个整数会消耗2个cpu周期,而如果这个数在0x00000004~0x00000008的内存上只需要一个cpu周期。为了加快内存的cpu访问,程序都使用了指针地址对齐概念。指针地址对齐就是指在分配堆中的内存时往往采用偶数倍或以2为指数倍的内存地址作为地址边界。几乎所有系统架构,包括mac os和ios,都使用了地址对齐概念。

运行这段代码后,得到了如下结果:

可以看到,a和b指针的最后4位都是0,虽然a只占用31个字节,但是a和b的地址却相差16个字节。因为ios中是以16个字节为内存分配边界的,或者说ios的指针地址对齐是以16个字节为对齐边界的。进一步说,ios中分配的内存地址最后4位永远都是0。

64位地址

iphone 5s中采用了arm64的cpu,同时也支持了64位的app。64位app中指针大小也扩大到64位,就是理论上可以支持最大264字节(达千万t字节)的内存地址空间。而对于大多数应用来说,这么大的地址空间完全是浪费的。也就是说,64位环境下,内存地址的前面很多位一般都是0。

标记(tagged)指针

由于指针地址对齐概念和64位超大地址的出现,指针地址仅仅作为内存的地址是比较浪费的,故此,可以在指针地址中保存或附加更多的信息。这就引入了标记(tagged)指针概念。标记(tagged)指针是指那些指针中包含特殊属性或信息的指针。其中指针对齐概念可以来标识一个指针是否是标记(tagged)指针及相关类型,64位的地址指针又可以提供保存额外信息的足够空间。如今,ios 7的64位环境和mac os 10.7(lion)中开始引入了标记(tagged)指针。

标记(tagged)指针对nsnumber的优化

标记(tagged)指针一个比较典型的应用就是nsnumber。在64位环境下,对于一般的数字,nsnumber不用再分配内存了。现在,看看nsnumber是如何运用标记(tagged)指针的:

在64位模拟器中运行后,得到了如下结果:

可以看出number3、number4和number9的值前4位都是0xb,后4位都是0x2(指针的tag),中间就是实际的取值。因此,这些nsnumber已经不需要再分配内存(指堆中内存)了,直接可以把实际的值保存到指针中,而无须再去访问堆中的数据。这无疑提高了内存访问速度和整体运算速度。

也就是说,标记(tagged)指针本身就可以表示一个nsnumber了,在64位环境下运行这段代码:

会输出如下结果:

那么,如果一个数超过了标记(tagged)指针所能表示的范围,系统会怎么处理?看看这段代码:

可以看出numberbig指针最后4位都是0,应该是分配在堆中的对象。因此,如果nsnumber超出了标记(tagged)指针所能表示的范围,系统会自动采用分配成对象,可以根据指针的最后4位是否为0来区分。

标记(tagged)指针对isa指针优化

查看nsobject类的头文件,你会发现这段定义:

所有类都继承自nsobject,因此每个对象都有一个isa指针指向它所属的类。在32位和64位的环境下, isa指针会产生不同的变化。

在32位环境下,对象的引用计数都保存在一个外部的表中,而对引用计数的增减操作都要先锁定这个表,操作完成后才解锁。这个效率是非常慢的。

而在64位环境下,isa也是64位,实际作为指针部分只用到其中的33位,剩余的部分会运用到标记(tagged)指针的概念。其中19位将保存对象的引用计数,这样对引用计数的操作只需要原子的修改这个指针即可。如果引用计数超出19位,才会将引用计数保存到外部表,而这种情况往往是很少的,因此效率将会大大提高。

 要点

(1)利用标记(tagged)指针,可以在指针地址中保存或附加更多的信息。

(2)利用标记(tagged)指针处理nsnumber,直接可以把实际的值保存到指针中,而无须再去访问堆中的数据,可提高内存访问速度和整体运算速度。

(3)在32位和64位的环境下,isa指针会产生不同的变化。在64位环境下,标记(tagged)指针可加快isa指针的处理效率。

继续阅读