A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© xqlyn123 中级黑马   /  2015-12-1 21:49  /  943 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

回顾

对象在内存中是对齐的,它们的地址总是指针大小的整数倍,通常为16的倍数。对象指针是一个64位的整数,而为了对齐,一些位将永远是零。

Tagged Pointer利用了这一现状,它使对象指针中非零位有了特殊的含义。在苹果的64位Objective-C实现中,若对象指针的最低有效位为1(即奇数),则该指针为Tagged Pointer。这种指针不通过解引用isa来获取其所属类,而是通过接下来三位的一个类表的索引。该索引是用来查找所属类是采用Tagged Pointer的哪个类。剩下的60位则留给类来使用。

Tagged Pointer有一个简单的应用,那就是NSNumber。它使用60位来存储数值。最低位置1。剩下3位为NSNumber的标志。在这个例子中,就可以存储任何所需内存小于60位的数值。

从外部看,Tagged Pointer很像一个对象。它能够响应消息,因为objc_msgSend可以识别Tagged Pointer。假设你调用integerValue,它将从那60位中提取数值并返回。这样,每访问一个对象,就省下了一次真正对象的内存分配,省下了一次间接取值的时间。同时引用计数可以是空指令,因为没有内存需要释放。对于常用的类,这将是一个巨大的性能提升。

NSString似乎并不适合Tagged Pointer,因为它的长度即可变,又可远远超过60位。然而,Tagged Pointer是可以与普通类共存的,即对一些值使用Tagged Pointer,另一些则使用一般的指针。例如,对于NSNumber,大于2^60-1的整数就不能采用Tagged Pointer来存储,而需要在内存中分配一个NSNumber的对象来存储。只要创建对象的代码编写正确,就没有问题。

NSString也是如此。对于那些所需内存小于60位的字符串,它可以创建一个Tagged Pointer。其余的则被放置在真正的NSString对象里。这使得常用的短字符串的性能得到明显的提升。实际代码就是如此吗?似乎Apple是这么认为的,因为他们这么做了并实现了它。

可能的实现方法

在看Apple的实现之前,让我们花点时间想想我们自己会如何实现这种字符串。最初想法很简单:置最低位为1,剩下的3位作为类的标志,60位为真正的数据。如何使用这60位是一个大问题。我们想要最大限度地利用这60位。

一个Cocoa字符串在概念上是一系列的Unicode字符。一共有1,112,064个有效的Unicode字符,所以需要21位代表一个字符。这意味着我们可以放两个字符在这60位里,浪费掉了18位。我们可以用一些额外的位来存储长度。所以一个采用Tagged Pointer的字符串可以是零个、一个或两个字符。然而被限制为只有两个字符的字符串似乎并没什么用。

NSString API实际上是基于UTF-16的实现,而不是直接基于Unicode。UTF-16用16位的序列值来表示Unicode。最常见的基本多文种平面(Basic Multilingual Plane,BMP)字符需要16位,字符编码超过65,535的则需要两个。我们可以放三个16位进60位,剩下12位。再借用一些表示长度的位,这将允许我们表示0-3个UTF-16字符。这将允许三个BMP字符,且其中一个字符可以超出BMP的范围。被限制为三个字符的字符串的使用仍然有限。

大多数APP里的字符串是ASCII。即使APP本地化到非ASCII语言,字符串也远远不止用于显示UI。它们用于URL组件、文件扩展名、对象键、属性列表值等等。UTF-8编码是一种ASCII兼容的编码,它将每一个ASCII字符编码为一个字节,用四字节编码其他Unicode字符。我们可以在60位里放七个字节,剩下的4位表示长度。这样这种字符串可以存储七个ASCII字符,或者少一些的非ASCII字符,这取决于这些字符是什么。

如果我们要优化ASCII,我们不妨放弃对Unicode的完整支持。毕竟包含非ASCII字符的字符串可以使用真正的NSString对象。ASCII是一个七位编码,如果我们给每个字符只分配7位会发生什么?让我们存储八个ASCII字符在这60位里,再用剩下的4位存储长度。这听起来很有用。在一个APP里可能有大量的字符串是纯ASCII并且只包含8个字符或更少。

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马