反汇编分析NSString,你印象中的NSString是这样吗

我们先来定义三个NSString

-(void) testNSString
{
    NSString* a = @"abc";
    NSString* b = [NSString stringWithUTF8String:"abc"];
    NSString* c = [@"ab" stringByAppendingString:@"c"];
}

大家都明白,a, b, c 都equalsToString:@"abc"。但是它们是指向同一个对象实例呢,还是根本就不是同一种类型。我们来看一下反汇编代码:

function_args`-[ViewController testNSString]:
    0x10bd48590 <+0>:   pushq  %rbp
    0x10bd48591 <+1>:   movq   %rsp, %rbp
    0x10bd48594 <+4>:   subq   $0x40, %rsp
    0x10bd48598 <+8>:   leaq   0x1ac9(%rip), %rax        ; @"abc"
    0x10bd4859f <+15>:  movq   %rdi, -0x8(%rbp)             ;        // save %rdi
    0x10bd485a3 <+19>:  movq   %rsi, -0x10(%rbp)         ;        // save %rsi
    0x10bd485a7 <+23>:  movq   %rax, %rdi
    0x10bd485aa <+26>:  callq  0x10bd48b1c               ; symbol stub for: objc_retain
    0x10bd485af <+31>:  leaq   0x616(%rip), %rdx         ; "abc"
    0x10bd485b6 <+38>:  movq   %rax, -0x18(%rbp)         ;        // NSString* a
->  0x10bd485ba <+42>:  movq   0x277f(%rip), %rax        ; (void *)0x000000010c05db20: NSString    // (Class)$rax = NSString
    0x10bd485c1 <+49>:  movq   0x2730(%rip), %rsi        ; "stringWithUTF8String:"
    0x10bd485c8 <+56>:  movq   %rax, %rdi
    0x10bd485cb <+59>:  callq  0x10bd48b0a               ; symbol stub for: objc_msgSend
    0x10bd485d0 <+64>:  movq   %rax, %rdi
    0x10bd485d3 <+67>:  callq  0x10bd48b28               ; symbol stub for: objc_retainAutoreleasedReturnValue
    0x10bd485d8 <+72>:  leaq   0x1aa9(%rip), %rdx        ; @"ab"
    0x10bd485df <+79>:  leaq   0x1ac2(%rip), %rsi        ; @"'c'"
    0x10bd485e6 <+86>:  movq   %rax, -0x20(%rbp)         ;        // NSString* b
    0x10bd485ea <+90>:  movq   0x270f(%rip), %rax        ; "stringByAppendingString:"
    0x10bd485f1 <+97>:  movq   %rdx, %rdi                 ;        // lea 0x1aa9(<+79>) -> %rdx -> %rdi
    0x10bd485f4 <+100>: movq   %rsi, -0x40(%rbp)
    0x10bd485f8 <+104>: movq   %rax, %rsi                 ;        // lea 0x270f(<+97>) -> %rax -> %rsi
    0x10bd485fb <+107>: movq   -0x40(%rbp), %rdx         ;        // 0x1ac2(<+86>) -> %rsi -> -0x40(%rbp) -> %rdx
    0x10bd485ff <+111>: callq  0x10bd48b0a               ; symbol stub for: objc_msgSend
    0x10bd48604 <+116>: movq   %rax, %rdi
    0x10bd48607 <+119>: callq  0x10bd48b28               ; symbol stub for: objc_retainAutoreleasedReturnValue
    0x10bd4860c <+124>: movq   %rax, -0x28(%rbp)         ;        // NSString* c
    0x10bd48610 <+128>: movq   -0x18(%rbp), %rax
    0x10bd48614 <+132>: movq   %rax, %rdi
    0x10bd48617 <+135>: callq  0x10bd48b22               ; symbol stub for: objc_retainAutorelease

NSString* a = @"abc"; 的反汇编片段如下:

    0x10bd48598 <+8>:   leaq   0x1ac9(%rip), %rax        ; @"abc"
    0x10bd4859f <+15>:  movq   %rdi, -0x8(%rbp)             ;        // save %rdi
    0x10bd485a3 <+19>:  movq   %rsi, -0x10(%rbp)         ;        // save %rsi
    0x10bd485a7 <+23>:  movq   %rax, %rdi
    0x10bd485aa <+26>:  callq  0x10bd48b1c               ; symbol stub for: objc_retain
    0x10bd485b6 <+38>:  movq   %rax, -0x18(%rbp)         ;        // NSString* a

如大家所知,@"abc"是一个全局实例,并且对其retain了一次(,因为是ARC环境),然后由a指向了它。

剩下的b和c却是从stringXXX调用中返回的,到底返回了什么,在没有调试过之前,你我都不好一口定金说它返回的就是什么。好就由调试器lldb告诉我们。

0x7fff53eb59a8: 0x00007fce7b706230            // NSString* c  {retainCount:2, hash:516202353} (__NSCFString *)
0x7fff53eb59b0: 0xa000000006362613            // NSString* b    {retainCount:-1, hash:516202353} (NSTaggedPointerString *) 
0x7fff53eb59b8: 0x000000010bd4a068            // NSString* a    {retainCount:-1, hash:516202353} (__NSCFConstantString *)

你断言对了吗?lldb给我们的信息真不少,真是可靠的小伙伴。

首先三者指向的目标不一样。

第二retainCount唯独c是可数的,表明它们生命周期不同。

第三它们的hash一致,表明它们的内容一样。

最后真真切切打印,它们不同类,是同宗。

 

我们先分析指针,第一列绿字地址,表明a,b,c是在堆栈中指针变量,而且地址相邻,和我的定义代码一致。然后看第二列地址,是a,b,c分别指向的地址,c指向堆栈下向某处,没错那就是堆;而b是一个无理头地址,看过我上一篇的介绍就会知道这是一个tagged指针;至于a中规中矩说出它就是全局实例。

原来a,b,c都不是同一样实例。但是结论可能定得太早。

现在我们再来分析它们的生命周期,它们的生命周期受谁掌控。c毫无疑问是受retain/release控制的。然而a,b岂不在retain的时候就被dealloc? 通过反汇编跟踪b@tagged指针忽略retain/release,a@__NSCFConstantString的retain/release不做任何处理。表明了a,b是与程序一样长寿的寿星公。那么这两位寿星公的关系几何呢,它们有上一腿吗?你会怀疑它们是同一身份吗?

不要净是我在说,如果你还不知道真相的话,也有兴趣弄明白就请阁下亲自验证一下了。

最后我们看一下lldb给出的NSString的类的父子链

NSString
NSMutableString
__NSCFString
__NSCFConstantString
NSString
NSTaggedPointerString

另外要清楚一点,objc中的继承是接口继承,不一定是成员继承,大家都知道声明都用@interface,class是可以动态构造的。

 

最后,又一次多谢大家的观看,更多的objc分析请关注后面的文章。

 

下一篇大家将会看到幽灵一样的指针TaggedPointer。

posted on 2016-01-09 17:19  bbqz007  阅读(1277)  评论(1编辑  收藏  举报