探索 NSDictionary
Exposing NSDictinoary
The Class
相当多的类都是 class clusters,当然 NSDictionary 也不例外,曾经一段时间 NSDictinoary 使用了 CFDictionary 作为它的默认实现。然而,在 iOS 6以后事情改变了。。。
1 | |
和 __NSArrayM 一样, __NSDictionaryI 也是在 CoreFoundation Framework 中:
1 | |
The Storage
Instance Creation
想要理解 __NSDictionaryI 在哪里保存内容,我们来看看对象创建的过程,只有一个类方法负责创建实例:__NSDictionaryI。根据 class-dump 的输出,方法有如下的签名:
1 | |
他需要5各参数,只有第一个是指名字,如果我们使用@selector 的方式来写的话,应该是这样 @selector(__new:::::)。前三个参数通过设置断点,然后看一下寄存器 x2、x3、x4的内容,看到他们是分别是:array 的 key、array 的 object、key(object) 的个数。注意和对外公布的 api 相比,参数的位置是互换的。
1 | |
其实参数定义为const id *或者const id []没有太大的关系。剩下的就是两个BOOL 类型的参数。第四个参数是指 key 是否需要被拷贝。第五个参数决定了参数是否不需要被 retain。我们可以重写该函数:
1 | |
Indexed ivars
除了函数+ __new:::::以外,malloc 和 calloc 并没有地方被调用。实际上,函数调用了 __CFAllocateObject2 方法,并且传递了 __NSDictionaryI 作为第一个参数,然后用需要的大小作为第二个参数。__CFAllocateObject2 实际上调用了 class_createInstance ,然后把同样的参数传给他。
class_createInstance(Class cls, size_t extraBytes) 调用了 _class_createInstanceFromZone 并且传个 nil 作为 zone 的参数。
1 | |
extreBytes 参数,calloc 函数调用确保了所有的 ivar 都是 0.
indexed ivars就是在普通的 ivar 后面
剩下的就是分配内存了
1 | |

关于 indexed ivar 有一些比较好玩的事情。
- 每一个实例都可以有不同数量的 extraBytes指向他
- 存取速度很快。
- 可以对 class-dump 这样的工具隐藏实现细节。
但是:在 arc 环境下是不能直接编译 class_createInstance,需要增加 -fno-objc-arc 标志位。。运行时没有保存 indexed ivars 信息。
key & 访问一个对象
C Code
1 | |
上述代码是从汇编反推出来的。
