我们先来定义三个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. POJ 3020 Antenna Placement(二分图 匈牙利算法)

    题目网址:  http://poj.org/problem?id=3020 题意: 用椭圆形去覆盖给出所有环(即图上的小圆点),有两种类型的椭圆形,左右朝向和上下朝向的,一个椭圆形最多可以覆盖相邻的两 ...

  2. .NET Core 3.0之深入源码理解ObjectPool(二)

    写在前面 前文主要介绍了ObjectPool的一些理论基础,本文主要从源码角度理解Microsoft.Extensions.ObjectPool是如何实现的.下图为其三大核心组件图: 核心组件 Obj ...

  3. 使用eclipse在tomcat中设置项目启动的虚拟路径

    很多时候我们在启动项目的时候都会在浏览器输入"localhost:+端口号+项目名称" 其实tomcat是可以省去这种麻烦的,通过设置项目的虚拟路径就可访问项目了 第一步 选择ec ...

  4. 面试 LockSupport.park()会释放锁资源吗?

    (手机横屏看源码更方便) 引子 大家知道,我最近在招人,今天遇到个同学,他的源码看过一些,然后我就开始了AQS连环问. 我:说说AQS的大致流程? 他:AQS包含一个状态变量,一个同步队列--bala ...

  5. krry-transfer ⏤ 基于 element 的升级版穿梭框组件发布到 npm 啦

    博客地址:https://ainyi.com/81 基于 element ui 的==升级版穿梭框组件==发布到 npm 啦 看过我之前博客的同学或许知道我之前写过关于 element 穿梭框组件重构 ...

  6. SpringBoot整合Redis(一)

    docker启动redis docker run -p 6379:6379 --name myredis redis 查看容器 [root@topcheer ~]# docker ps -l CONT ...

  7. UART和RS232/RS485的关系是什么?

    串口通讯是电子工程师和嵌入式开发工程师面对的最基本问题,RS232则是其中最简单最常用的通讯方式.但是初学者往往搞不清有关的名词如UART和RS232或RS485之间是什么关系,因为它们经常被放到语句 ...

  8. Java 干货之深入理解Java泛型

    一般的类和方法,只能使用具体的类型,要么是基本类型,要么是自定义的类.如果要编写可以应用多中类型的代码,这种刻板的限制对代码得束缚会就会很大. ---<Thinking in Java> ...

  9. 洛谷P5522 【[yLOI2019] 棠梨煎雪】

    区间操作考虑用线段树维护. 建\(n*2\)棵线段树,前\(n\)棵线段树维护每个串的第i位是否是0. 后\(n\)棵线段树维护每个串的第i位是否是1. 如果是问号的话,直接跳过就好(通过1和0能看出 ...

  10. CSP-S:追忆

    Warning:这一篇极其中二,开了那个大会莫名有感而发. 模拟测试17那套题啊... 开的这个大会为什么弄得我退役感如此强烈... 早就想收藏了,还是记下来吧 <入阵曲> 丹青千秋酿, ...