Swift5.x入门15--汇编分析String与Array

首先定义一个字符串,如下所示:

var str1 = "0123456789"
print(MemoryLayout.stride(ofValue: str1)) //16
  • 易知str1字符串在内存中占用16个字节;
  • 下断点,进入汇编代码:
Snip20210803_111.png
  • 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,以此类推,如下所示:
    Snip20210803_112.png
  • 每一个字节存储一个字符串字符,至于最后一个字节中存储的是ea,其中a表示字符串的长度为10,e表示字符串类型 ,此类型为将字符串内存存储在字符串变量的内存地址中;
  • 为了验证上面的结论,将str1 = "0123456789ABCDE",然后汇编分析如下:
Snip20210803_113.png
  • 结果与上面的类似,最后一个字节内容ef,其中f表示字符串长度为15,因为字符串本身只占16个字节,最后一个字节用来存储字符串的长度与类型信息,所以此种类型的字符串最大长度为15个字符,超过15个字符,其类型就会发生变化了;

现将str1 = "0123456789ABCDEFGHJKL",长度超过15个字符;汇编分析如下:

Snip20210803_117.png
  • leaq 0x61(%rip), %raxmovq %rax, %rdi :将字符串0123456789ABCDEFGHJKL的内存地址写入rax寄存器,然后再写入rdi寄存器;
  • movl $0x15, %eaxmovq %rax, %rsi:将字符串的长度21写入eax(rdx)寄存器,然后再写入rsi寄存器;
  • rdi与rsi中的内容分别为字符串的内存地址和字符串的长度;
  • callq 0x100003f64,调用初始化字符串的函数,传入上述的两个参数;
  • 进入到初始化字符串的函数的汇编实现:
Snip20210803_119.png
  • 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;

总结:

Snip20210803_121.png
  • 当字符串的长度小于等于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变量的内存地址,如下所示:
Snip20210803_123.png
  • 0x100008058就是arr变量的内存地址;
  • 0x000000010065a2f0是数组对象的真实内存地址;
  • 总结如下:
Snip20210803_124.png

你可能感兴趣的:(Swift5.x入门15--汇编分析String与Array)