• 反汇编分析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。

  • 相关阅读:
    详述JavaScript实现继承的几种方式
    理解javascript函数调用和“this”
    React-Native 组件开发方法
    React Native 中 component 生命周期
    React-Native 样式指南
    React Native 之flex布局
    转每天一个linux命令(5):rm 命令
    [Python爬虫] 在Windows下安装PhantomJS和CasperJS及入门介绍(上)
    转每天一个linux命令(4):mkdir命令
    转每天一个linux命令(3):pwd命令
  • 原文地址:https://www.cnblogs.com/bbqzsl/p/5110343.html
Copyright © 2020-2023  润新知