我们先来定义三个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 <+>: pushq %rbp
0x10bd48591 <+>: movq %rsp, %rbp
0x10bd48594 <+>: subq $0x40, %rsp
0x10bd48598 <+>: leaq 0x1ac9(%rip), %rax ; @"abc"
0x10bd4859f <+>: movq %rdi, -0x8(%rbp) ; // save %rdi
0x10bd485a3 <+>: movq %rsi, -0x10(%rbp) ; // save %rsi
0x10bd485a7 <+>: movq %rax, %rdi
0x10bd485aa <+>: callq 0x10bd48b1c ; symbol stub for: objc_retain
0x10bd485af <+>: leaq 0x616(%rip), %rdx ; "abc"
0x10bd485b6 <+>: movq %rax, -0x18(%rbp) ; // NSString* a
-> 0x10bd485ba <+>: movq 0x277f(%rip), %rax ; (void *)0x000000010c05db20: NSString // (Class)$rax = NSString
0x10bd485c1 <+>: movq 0x2730(%rip), %rsi ; "stringWithUTF8String:"
0x10bd485c8 <+>: movq %rax, %rdi
0x10bd485cb <+>: callq 0x10bd48b0a ; symbol stub for: objc_msgSend
0x10bd485d0 <+>: movq %rax, %rdi
0x10bd485d3 <+>: callq 0x10bd48b28 ; symbol stub for: objc_retainAutoreleasedReturnValue
0x10bd485d8 <+>: leaq 0x1aa9(%rip), %rdx ; @"ab"
0x10bd485df <+>: leaq 0x1ac2(%rip), %rsi ; @"'c'"
0x10bd485e6 <+>: movq %rax, -0x20(%rbp) ; // NSString* b
0x10bd485ea <+>: movq 0x270f(%rip), %rax ; "stringByAppendingString:"
0x10bd485f1 <+>: movq %rdx, %rdi ; // lea 0x1aa9(<+79>) -> %rdx -> %rdi
0x10bd485f4 <+>: movq %rsi, -0x40(%rbp)
0x10bd485f8 <+>: movq %rax, %rsi ; // lea 0x270f(<+97>) -> %rax -> %rsi
0x10bd485fb <+>: movq -0x40(%rbp), %rdx ; // 0x1ac2(<+86>) -> %rsi -> -0x40(%rbp) -> %rdx
0x10bd485ff <+>: callq 0x10bd48b0a ; symbol stub for: objc_msgSend
0x10bd48604 <+>: movq %rax, %rdi
0x10bd48607 <+>: callq 0x10bd48b28 ; symbol stub for: objc_retainAutoreleasedReturnValue
0x10bd4860c <+>: movq %rax, -0x28(%rbp) ; // NSString* c
0x10bd48610 <+>: movq -0x18(%rbp), %rax
0x10bd48614 <+>: movq %rax, %rdi
0x10bd48617 <+>: callq 0x10bd48b22 ; symbol stub for: objc_retainAutorelease

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

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

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

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

0x7fff53eb59a8: 0x00007fce7b706230            // NSString* c  {retainCount:, hash:} (__NSCFString *)
0x7fff53eb59b0: 0xa000000006362613 // NSString* b {retainCount:-, hash:} (NSTaggedPointerString *)
0x7fff53eb59b8: 0x000000010bd4a068 // NSString* a {retainCount:-, hash:} (__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。

反汇编分析NSString,你印象中的NSString是这样吗的更多相关文章

  1. 九、Foundation框架中的NSString常用方法

    一.NSString的创建 方式1创建常量字符串 NSString *st = @"this is string!"; //这种方式创建的字符串不需要释放 方式2创建空字符串,给予 ...

  2. (转载)OC学习篇之---Foundation框架中的NSString对象和NSMutableString对象

    在之前的一篇文章中我们说到了Foundation框架中的NSObject对象,那么今天在在来继续看一下Foundation框架中的常用对象:NSString和NSMutableString. 在OC中 ...

  3. OC学习篇之---Foundation框架中的NSString对象和NSMutableString对象

    今天在在来继续看一下Foundation框架中的常用对象:NSString和NSMutableString 在OC中NSString对象是不可变的,和Java中的String一样的,而NSMutabl ...

  4. objc反汇编分析,block函数块为何物?

    上一篇向大家介绍了__block变量的反汇编和它的伪代码,本篇函数块block,通常定义成原型(^){},它在反汇编中是什么东西. 我们先定义将要反汇编的例子,为减少篇幅例子采用non-arc环境. ...

  5. Linux下简单C语言小程序的反汇编分析

    韩洋原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 写在开始,本文为因为参加MOO ...

  6. 反汇编分析objc函数枢纽objc_msgSend

    在分析objc_msgSend之前,先来搞清楚另一个问题. 函数是什么?可能会答 void foo(void) {} 像这样就是一个函数.或者函数包括函数原型和函数定义,是一段执行某样功能的机器代码. ...

  7. Hbase源码分析:Hbase UI中Requests Per Second的具体含义

    Hbase源码分析:Hbase UI中Requests Per Second的具体含义 让运维加监控,被问到Requests Per Second(见下图)的具体含义是什么?我一时竟回答不上来,虽然大 ...

  8. 深度分析如何在Hadoop中控制Map的数量

    深度分析如何在Hadoop中控制Map的数量 guibin.beijing@gmail.com 很多文档中描述,Mapper的数量在默认情况下不可直接控制干预,因为Mapper的数量由输入的大小和个数 ...

  9. 零元学Expression Blend 4 - Chapter 7 什麽?影片不再是印象中的方框框!!!看Blend 4如何把影片镶入字里

    原文:零元学Expression Blend 4 - Chapter 7 什麽?影片不再是印象中的方框框!!!看Blend 4如何把影片镶入字里 本章将教大家如何在Blend 4里新增Media El ...

随机推荐

  1. netty中Pipeline的ChannelHandler执行顺序案例详解

    一.netty的Pipeline模型 netty的Pipeline模型用的是责任链设计模式,当boss线程监控到绑定端口上有accept事件,此时会为该socket连接实例化Pipeline,并将In ...

  2. 楼上请让路 RoarCTF2019 Writeup

    Misc 签到题 RoarCTF{签到!!!} 黄金六年 文件尾部有一段base64,解码为16进制可以看到是一个压缩包 打开压缩包需要密码 使用pr抽帧 可以看到部分帧中有二维码,依次扫码即可得到k ...

  3. 05 Node.js学习笔记之发送文件数据

    这章学习在NodeJs中如何将Html文件发送到客户端上,以及定义Content-Type内容类型 //1.载入http和fs模块 var http=require("http") ...

  4. Mybaits 源码解析 (一)----- 搭建一个mybatis框架(MyBatis HelloWorld)

    源码分析之前先搭一个mybatis的demo,这个在看源码的时候能起到了很大的作用,因为在看源码的时候,会恍然大悟,为什么要这么配置,为什么要这么写.(老鸟可以跳过这篇) 开发环境的准备 创建mave ...

  5. 玩转OneNET物联网平台之HTTP服务③ —— OneNet智能灯 HTTP版本

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  6. Shiro权限管理框架(四):深入分析Shiro中的Session管理

    其实关于Shiro的一些学习笔记很早就该写了,因为懒癌和拖延症晚期一直没有落实,直到今天公司的一个项目碰到了在集群环境的单点登录频繁掉线的问题,为了解决这个问题,Shiro相关的文档和教程没少翻.最后 ...

  7. Visual Studio Code 添加C/C++编译功能

    VS Code作为一个文本/代码编辑器,相较于VS比较轻量化,而且可以支持C/C++.Python等多种语言,并具有丰富的拓展模块. 但是作为一个编辑器,在VS Code上安装C/C++模块之后,并不 ...

  8. Element filtername is not allowed here-web.xml version="3.0"-intellij idea

    Element filtername is not allowed here-web.xml version="3.0"-intellij idea Intellij IDEA 报 ...

  9. 使用grep过滤make的输出内容

    make的输出内容其实分为两种,有些是到标准输出,有些是到标准错误,由于标准输出和标准错误默认都是屏幕,所以平时区分不出来, 实际上一般是error和warning信息到标准错误,其余的到标准输出. ...

  10. 【Leetcode 做题学算法周刊】第二期

    首发于微信公众号<前端成长记>,写于 2019.11.05 背景 本文记录刷题过程中的整个思考过程,以供参考.主要内容涵盖: 题目分析设想 编写代码验证 查阅他人解法 思考总结 目录 20 ...