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

简介:

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

建议10:在64位环境下尽可能利用标记指针

在Mac OS X 10.6(雪豹)中开始支持64位,如今最新版本iPhone 5s也开始采用Arm64架构。在64位化的过程中,其中一个比较关键的改进就是,Mac OS 10.7(美洲虎)和iOS 7的64位环境先后引入了标记(Tagged)指针。
下面就简单地来介绍一下标记(Tagged)指针。在介绍标记(Tagged)指针之前有必要介绍一下指针地址对齐概念和64位环境的一些变化。

  1. 指针地址对齐
    在32位环境下,要读取一个32位整数,如果这个32位整数在内存地址为0x00000002-0x00000006(仅作举例,这个地址一般是被系统保留的)的内存上,读取这个整数会消耗2个CPU周期,而如果这个数在0x00000004~0x00000008的内存上只需要一个CPU周期。为了加快内存的CPU访问,程序都使用了指针地址对齐概念。指针地址对齐就是指在分配堆中的内存时往往采用偶数倍或以2为指数倍的内存地址作为地址边界。几乎所有系统架构,包括Mac OS和iOS,都使用了地址对齐概念。
void *a = malloc(1);
void *b = malloc(3);
NSLog(@"a: %p",a);
NSLog(@"b: %p",b);

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

a: 0x8c11e20
b: 0x8c11e30

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

  1. 64位地址
    iPhone 5s中采用了Arm64的CPU,同时也支持了64位的App。64位App中指针大小也扩大到64位,就是理论上可以支持最大264字节(达千万T字节)的内存地址空间。而对于大多数应用来说,这么大的地址空间完全是浪费的。也就是说,64位环境下,内存地址的前面很多位一般都是0。
  2. 标记(Tagged)指针
    由于指针地址对齐概念和64位超大地址的出现,指针地址仅仅作为内存的地址是比较浪费的,故此,可以在指针地址中保存或附加更多的信息。这就引入了标记(Tagged)指针概念。标记(Tagged)指针是指那些指针中包含特殊属性或信息的指针。其中指针对齐概念可以来标识一个指针是否是标记(Tagged)指针及相关类型,64位的地址指针又可以提供保存额外信息的足够空间。如今,iOS 7的64位环境和Mac OS 10.7(Lion)中开始引入了标记(Tagged)指针。
  3. 标记(Tagged)指针对NSNumber的优化
    标记(Tagged)指针一个比较典型的应用就是NSNumber。在64位环境下,对于一般的数字,NSNumber不用再分配内存了。现在,看看NSNumber是如何运用标记(Tagged)指针的:
NSNumber *number3 = @3;
NSNumber *number4 = @4;
NSNumber *number9 = @9;
NSLog(@"number3 pointer is %p", number3);
NSLog(@"number4 pointer is %p", number4);
NSLog(@"number9 pointer is %p", number9);

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

number3 pointer is 0xb000000000000032
number4 pointer is 0xb000000000000042
number9 pointer is 0xb000000000000092

可以看出number3、number4和number9的值前4位都是0xb,后4位都是0x2(指针的Tag),中间就是实际的取值。因此,这些NSNumber已经不需要再分配内存(指堆中内存)了,直接可以把实际的值保存到指针中,而无须再去访问堆中的数据。这无疑提高了内存访问速度和整体运算速度。
也就是说,标记(Tagged)指针本身就可以表示一个NSNumber了,在64位环境下运行这段代码:

NSLog(@"0xb000000000000052's class is %@",[(NSNumber*)0xb000000000000052 class]);

会输出如下结果:

0xb000000000000052's class is __NSCFNumber

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

NSNumber *numberBig = @(0x1234567890ABCDEF);
NSLog(@"numberBig pointer is %p", numberBig);

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

numberBig pointer is 0x1094026a0

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

  1. 标记(Tagged)指针对isa指针优化
    查看NSObject类的头文件,你会发现这段定义:
@interface NSObject <NSObject> {
    Class isa;
}

所有类都继承自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指针的处理效率。

相关文章
|
3月前
|
存储 安全 编译器
【指针:掌握它,让你的程序如虎添翼】
【指针:掌握它,让你的程序如虎添翼】
|
5月前
|
机器学习/深度学习 存储 人工智能
【网安AIGC专题11.8】论文15 ChatGPT在软件工程中的全面作用:程序语法(AST生成、表达式匹配) 静态行为、动态分析(数据依赖和污点分析、指针分析) 提示设计(角色提示、指令提示)
【网安AIGC专题11.8】论文15 ChatGPT在软件工程中的全面作用:程序语法(AST生成、表达式匹配) 静态行为、动态分析(数据依赖和污点分析、指针分析) 提示设计(角色提示、指令提示)
73 0
编写程序简单实现计算器:加减乘除功能(函数指针数组的写法)
编写程序简单实现计算器:加减乘除功能(函数指针数组的写法)
101 0
编写程序简单实现计算器:加减乘除功能(函数指针数组的写法)
|
存储 C语言 C++
【C语言】大厂指针笔试题(1码+1图)详解——程序结果判断题
【C语言】大厂指针笔试题(1码+1图)详解——程序结果判断题
【C语言】大厂指针笔试题(1码+1图)详解——程序结果判断题