NSArray创建背后的细节--类簇

2016-10-19 iOS

类簇

类簇是一种在 Foundation 中的一种设计模式,这个模式下,一些结构、功能相似,有具体实现方法的私有子类被隐藏地组合起来,通过一个抽象类来创建,比如我们日常使用的 NSArray,NSString和NSDictionary 都是通过类簇实现的。
我们分别调用 NSArray 的几个方法来创建实例对象:

(lldb) po [NSArray array]
<__NSArray0 0x600000017670>(
)
(lldb) po [NSArray arrayWithObject:@"Hello,Zie"];
<__NSSingleObjectArrayI 0x600000017680>(
Hello,Zie
)
po [NSArray arrayWithObjects:@1,@2, nil];
<__NSArrayI 0x100500050>(
1,
2
)

通过打印可以看到由NSArray ,创建的对象并不是NSArray本身,有可能是 __NSArray0__NSSingleObjectArrayI__NSArrayI ,这里 NSArray 就是那个抽象类,而被创建出来那些奇奇怪的类就是作为具体的实现类,同时是内部私有的。

[NSArray alloc]

直接调用 [NSArray alloc] ,会发现返回了一个__NSPlaceholderArray对象,并且每次调用都返回相同的地址。

我们知道一个对象的初始化会调用 alloc 会,继而调用 allocWithZone:
下符号断点[NSArray allocWithZone:],就可以看到相关的实现:

CoreFoundation`+[NSArray allocWithZone:]:
    0x101fd20d0 <+0>:   pushq  %rbp
    0x101fd20d1 <+1>:   movq   %rsp, %rbp
    0x101fd20d4 <+4>:   pushq  %r15
    0x101fd20d6 <+6>:   pushq  %r14
    0x101fd20d8 <+8>:   pushq  %rbx
    0x101fd20d9 <+9>:   subq   $0x18, %rsp
    0x101fd20dd <+13>:  movq   %rdx, %r14
    0x101fd20e0 <+16>:  movq   %rdi, %rbx
    0x101fd20e3 <+19>:  movq   0x3a970e(%rip), %rdi      ; (void *)0x000000010237dc58: NSArray
    0x101fd20ea <+26>:  movq   0x3a88d7(%rip), %r15      ; "self"
    0x101fd20f1 <+33>:  movq   %r15, %rsi
    0x101fd20f4 <+36>:  callq  *0x371126(%rip)           ; (void *)0x0000000101b39ac0: objc_msgSend
    0x101fd20fa <+42>:  cmpq   %rbx, %rax
    0x101fd20fd <+45>:  je     0x101fd2141               ; <+113>
    0x101fd20ff <+47>:  movq   0x3a9622(%rip), %rdi      ; (void *)0x000000010237dcd0: NSMutableArray
    0x101fd2106 <+54>:  movq   %r15, %rsi
    0x101fd2109 <+57>:  callq  *0x371111(%rip)           ; (void *)0x0000000101b39ac0: objc_msgSend
    0x101fd210f <+63>:  cmpq   %rbx, %rax
    0x101fd2112 <+66>:  je     0x101fd2151               ; <+129>
    0x101fd2114 <+68>:  movq   %rbx, -0x28(%rbp)
    0x101fd2118 <+72>:  movq   0x3a9a79(%rip), %rax      ; (void *)0x000000010237dd20: NSArray
    0x101fd211f <+79>:  movq   %rax, -0x20(%rbp)
    0x101fd2123 <+83>:  movq   0x3a7b46(%rip), %rsi      ; "allocWithZone:"
    0x101fd212a <+90>:  leaq   -0x28(%rbp), %rdi
    0x101fd212e <+94>:  movq   %r14, %rdx
    0x101fd2131 <+97>:  callq  0x10217db80               ; symbol stub for: objc_msgSendSuper2
    0x101fd2136 <+102>: addq   $0x18, %rsp
    0x101fd213a <+106>: popq   %rbx
    0x101fd213b <+107>: popq   %r14
    0x101fd213d <+109>: popq   %r15
    0x101fd213f <+111>: popq   %rbp
    0x101fd2140 <+112>: retq   
->  0x101fd2141 <+113>: movq   0x3a96b8(%rip), %rdi      ; (void *)0x000000010237dcf8: __NSPlaceholderArray
    0x101fd2148 <+120>: movq   0x3a8769(%rip), %rsi      ; "immutablePlaceholder"
    0x101fd214f <+127>: jmp    0x101fd215f               ; <+143>
    0x101fd2151 <+129>: movq   0x3a96a8(%rip), %rdi      ; (void *)0x000000010237dcf8: __NSPlaceholderArray
    0x101fd2158 <+136>: movq   0x3a8781(%rip), %rsi      ; "mutablePlaceholder"
    0x101fd215f <+143>: addq   $0x18, %rsp
    0x101fd2163 <+147>: popq   %rbx
    0x101fd2164 <+148>: popq   %r14
    0x101fd2166 <+150>: popq   %r15
    0x101fd2168 <+152>: popq   %rbp
    0x101fd2169 <+153>: jmpq   *0x3710b1(%rip)           ; (void *)0x0000000101b39ac0: objc_msgSend
    0x101fd216f <+159>: nop    

写成代码的话,大概是这个样子:

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    if (self == [NSArray self]) {  
        return [__NSPlaceholderArray immutablePlaceholder];
    }
    if( self == [NSMutableArray self]) {
        return [__NSPlaceholderArray mutablePlaceholder];
    }
    return [super allocWithZone:zone];
}

总结起来就是:
如果是 NSArray 调用 alloc 方法,就会返回 [__NSPlaceholderArray immutablePlaceholder]
如果是 NSMutableArray 调用 alloc 方法,就会返回 [__NSPlaceholderArray mutablePlaceholder]

关于类簇的实现,可以看看 sunnyxx 的这篇文章

Comments
Write a Comment