Preface
数组,是我们在开发中经常使用的数据结构,其使用一段连续的内存空间存储。在 Obj-C 中,数组的类型分为 NSArray
不可变数组
Objective-C
NSArray & NSMutableArray
我们都知道,NSArray
是 Obj-C 中的不可变数组,而 NSMutableArray
是 Obj-C 的可变数组。这里我将 Obj-C 中几乎所有可能涉及到的数组对象,进行尝试打印其类别信息:
NSLog(@"%@", [[NSMutableArray alloc] class]); // __NSPlaceholderArray
NSLog(@"%@", [[NSArray alloc] class]); // __NSPlaceholderArray
NSLog(@"%@", [[[NSArray alloc] class] superclass]); // NSMutableArray
NSLog(@"%@", [[[[NSArray alloc] class] superclass] superclass]); // NSArray
NSLog(@"%@", [[[[[NSArray alloc] class] superclass] superclass] superclass]); // NSObject
NSLog(@"%@", [[NSArray array] class]); // __NSArray0
NSLog(@"%@", [[[NSArray alloc] init] class]); // __NSArray0
NSLog(@"%@", [[[[NSArray alloc] init] class] superclass]); // NSArray
NSLog(@"%@", [[NSMutableArray array] class]); // __NSArrayM
NSLog(@"%@", [[[NSMutableArray array] class] superclass]); // NSMutableArray
NSLog(@"%@", [@[@1] class]); // __NSSingleObjectArrayI
NSLog(@"%@", [[@[@1] class] superclass]); // NSArray
NSLog(@"%@", [@[@1, @2] class]); // __NSArrayI
NSLog(@"%@", [[@[@1, @2] class] superclass]); // NSArray
NSMutableArray *arr = [NSMutableArray array];
NSLog(@"%@", [arr class]); // __NSArrayM
[arr addObject:@1];
NSLog(@"%@", [arr class]); // __NSArrayM
由以上结果,我们可得出下图的继承树:
显而易见,NSArray
和 NSMutableArray
均属于类簇(Class Cluster),Obj-C 中所广泛使用的一种抽象工厂设计模式。这种设计使得类型对外暴露的接口得到统一,隐藏内部的具体实现类。
__NSPlacehodlerArray
__NSPlacehodlerArray
顾名思义即「占位」数组。无论我们要创建 NSArray
还是 NSMutableArray
,当调用 alloc
时,均返回了 __NSPlaceholderArray
类型的对象:
NSLog(@"%@", [[NSMutableArray alloc] class]); // __NSPlaceholderArray
NSLog(@"%@", [[NSArray alloc] class]); // __NSPlaceholderArray
而每次使用的 __NSPlaceholderArray
id arr1 = [NSArray alloc];
id arr2 = [NSArray alloc];
id arr3 = [NSArray alloc];
id arr4 = [NSArray alloc];
id arr5 = [NSArray alloc];
id marr1 = [NSMutableArray alloc];
id marr2 = [NSMutableArray alloc];
id marr3 = [NSMutableArray alloc];
id marr4 = [NSMutableArray alloc];
id marr5 = [NSMutableArray alloc];
// 0x7fff90a905b8 - 0x7fff90a905b8 - 0x7fff90a905b8 - 0x7fff90a905b8 - 0x7fff90a905b8
NSLog(@"%p - %p - %p - %p - %p", arr1, arr2, arr3, arr4, arr5);
// 0x7fff90a905c8 - 0x7fff90a905c8 - 0x7fff90a905c8 - 0x7fff90a905c8 - 0x7fff90a905c8
NSLog(@"%p - %p - %p - %p - %p", marr1, marr2, marr3, marr4, marr5);
// 0x7fff90a905c8 - 0x7fff90a905b8 == 16
那么问题来了,__NSPlaceholderArray
内部是如何区分将要初始化的数组类型呢?
For historical reasons,
alloc
invokesallocWithZone:
.
根据官方文档中的提示,alloc
方法由于历史原因,内部仍然调用了 allocWithZone:.
。我们通过 LLDB breakpoint set --name "+[NSArray allocWithZone:]"
打个断点
CoreFoundation`+[NSArray allocWithZone:]:
-> 0x7fff38ecb971 <+0>: pushq %rbp
...
0x7fff38ecb9e8 <+119>: jmp 0x7fff38ecba0c ; __NSArrayImmutablePlaceholder
...
0x7fff38ecba02 <+145>: jmp 0x7fff38ee4a8d ; __NSArrayMutablePlaceholder
CoreFoundation`__NSArrayImmutablePlaceholder:
-> 0x7fff38ecba0c <+0>: leaq 0x57bc4ba5(%rip), %rax ; ___immutablePlaceholderArray
0x7fff38ecba13 <+7>: retq
CoreFoundation`__NSArrayMutablePlaceholder:
-> 0x7fff38ee4a8d <+0>: leaq 0x57babb34(%rip), %rax ; ___mutablePlaceholderArray
0x7fff38ee4a94 <+7>: retq