首先定义一个字符串,如下所示:
var str1 = "0123456789"
print(MemoryLayout.stride(ofValue: str1)) //16
- 易知str1字符串在内存中占用16个字节;
- 下断点,进入汇编代码:
-
movq %rax, 0x40ac(%rip)
与movq %rdx, 0x40ad(%rip)
是分别向字符串变量str1的前后8个字节分别写入rax与rdx寄存器中的内容,刚好16个字节的内容; -
0x40ac(%rip)
是字符串变量str1的首地址 = (rip+ 0x40ac)= (0x100003f64+ 0x40ac)=0x100008010 - x/4gx 0x100008010 就是读取字符串中内容,发现与寄存器写入字符串变量str1内存地址中的内容完全相同;
-
0x3736353433323130 0xea00000000003938
,由ASCII表可知
30代表1,31代表2,以此类推,如下所示:
- 每一个字节存储一个字符串字符,至于最后一个字节中存储的是
ea
,其中a
表示字符串的长度为10,e
表示字符串类型 ,此类型为将字符串内存存储在字符串变量的内存地址中; - 为了验证上面的结论,将str1 = "0123456789ABCDE",然后汇编分析如下:
- 结果与上面的类似,最后一个字节内容
ef
,其中f表示字符串长度为15,因为字符串本身只占16个字节,最后一个字节用来存储字符串的长度与类型信息,所以此种类型的字符串最大长度为15个字符
,超过15个字符,其类型就会发生变化了;
现将str1 = "0123456789ABCDEFGHJKL",长度超过15个字符;汇编分析如下:
-
leaq 0x61(%rip), %rax
与movq %rax, %rdi
:将字符串0123456789ABCDEFGHJKL
的内存地址写入rax寄存器,然后再写入rdi寄存器; -
movl $0x15, %eax
与movq %rax, %rsi
:将字符串的长度21写入eax(rdx)寄存器,然后再写入rsi寄存器; - rdi与rsi中的内容分别为字符串的内存地址和字符串的长度;
-
callq 0x100003f64
,调用初始化字符串的函数,传入上述的两个参数; - 进入到初始化字符串的函数的汇编实现:
-
cmpq $0xf, %rsi
:将字符串的长度与立即数15进行比较,然后执行不同的逻辑,这也就证明了字符串长度是否大于15,其在内存中分配方式是不同的; -
movabsq $0x7fffffffffffffe0, %rdx
:将0x7fffffffffffffe0写入rdx寄存器; -
addq %rdx, %rdi
:rdx = rdx + rdi = 0x7fffffffffffffe0+字符串的内存地址; - 字符串初始化函数执行完成之后:
- 执行
movq %rdx, 0x40bd(%rip)
即将(0x7fffffffffffffe0+字符串的内存地址)存入字符串内存地址的后8个字节中;即0x7fffffffffffffe0+字符串的内存地址 =0x8000000100003f70
,所以
字符串的内存地址 = 0x8000000100003f70 - 0x7fffffffffffffe0 =0x100003F90
,这与上面的LLDB调试的字符串的内存地址完全吻合; - 字符串内存地址前8个字节的内容为
0xd000000000000015
,其实存储的是字符串的长度为21;
总结:
- 当字符串的长度小于等于15时,字符串内容直接存放在字符串变量的内存地址中;
- 当字符串的长度大于15时,字符串内容存放在__TEXT.cstring中,即常量区中;字符串的地址值信息存放在字符串变量的后8个字节中,前8个字节存放字符串的长度信息;
str1.append("G") 字符串拼接操作
- 当字符串的长度小于等于15时,进行字符串拼接操作后,若字符串的长度依然小于等于15时,字符串内容直接依然存放在字符串变量的内存地址中;
- 当字符串的长度小于等于15时,进行字符串拼接操作后,若字符串的长度大于15时,会开辟堆空间;
- 当字符串的长度大于15时,进行字符串拼接操作后,会开辟堆空间;
- append函数,开辟堆空间,实例化创建拼接后的字符串,然后将拼接后的字符串的堆内存地址,存放入字符串变量的后8个字节中,前8个字节存放字符串的长度信息;
dyld_stub_binder
- 符号的延迟绑定是通过dyld_stub_binder完成;
Array数组
var arr = [1,2,3,4]
print(MemoryLayout.stride(ofValue: arr)) //8
- arr变量占用8个字节,但数组中存放了4个Int数据,至少占32个字节,所以可大胆猜测, arr变量的内存地址中存放着数组的地址信息;
- 首先来获取 arr变量的内存地址,如下所示:
-
0x100008058
就是arr变量的内存地址; -
0x000000010065a2f0
是数组对象的真实内存地址; - 总结如下: