站在汇编角度深入了解 Swift(十三)
2020/4/7 23:01:33
本文主要是介绍站在汇编角度深入了解 Swift(十三),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
关于 String 的思考
- 1个 String 变量占用多少内存?
- 下面2个 String 变量,底层存储有什么不同?
var str1 = "0123456789" var str2 = "0123456789012345" 复制代码
- 如果对 String 进行拼接操作,String 变量的存储会发生什么变化?
str1.append("01234") str1.append("5") str2.append("6") str2.append("7") -------------------执行结果------------------- (lldb) x/5wg 0x7ffeefbff420 0x7ffeefbff420: 0x3736353433323130 0xea00000000003938 0x7ffeefbff430: 0x0000000000000000 0x0000000000000000 0x7ffeefbff440: 0x00007ffeefbff460 (lldb) x/5wg 0x7ffeefbff420 0x7ffeefbff420: 0x3736353433323130 0xef34333231303938 0x7ffeefbff430: 0x0000000000000000 0x0000000000000000 0x7ffeefbff440: 0x00007ffeefbff460 (lldb) x/5wg 0x7ffeefbff420 0x7ffeefbff420: 0xf000000000000010 0x000000010073f900 (lldb) x/5wg 0x7ffeefbff410 0x7ffeefbff410: 0xd000000000000010 0x8000000100014890 0x7ffeefbff420: 0xf000000000000010 0x000000010280e670 0x7ffeefbff430: 0x0000000000000000 (lldb) x/5wg 0x7ffeefbff410 0x7ffeefbff410: 0xf000000000000011 0x000000010280f1a0 // rdx = 0x000000010280f1a0 + 0x8000000000000020 // rdx = 0x800000010280f1c0 (lldb) x/5wg 0x000000010280f1c0 0x10280f1c0: 0x3736353433323130 0x3534333231303938 0x10280f1d0: 0x6f6e206567610036 0x0000000000000000 复制代码
汇编探究
var str1 = "0123456789" var str2 = "0123456789012345" printMemory(t1: str1) printMemory(t1: str2) ---------------------执行结果--------------------- 16 16 8 16 16 8 复制代码
所以第一题的答案是16个字节
small string
- 类似于 OC 中的 taggedPointer
- 地址存储的就是值
var str2 = "0123456789" ------------------执行结果----------------- 0x7ffeefbff420 0x7ffeefbff420: 0x3736353433323130 0xea00000000003938 复制代码
可以看出来在内存中存储的是字符串的值,后面字节存储的是字符串的升序
string
swiftstudy`test46(): // rdi = 0x00000001000148c0 "0123456789012345" // 0x1000148c0: 0x3736353433323130 0x3534333231303938 // 0x1000148d0: 0x0000000000000000 0x0000000000000000 // rdi 存储的是字符串的真实地址,可以看出来是放在全局区的 leaq 0xd08a(%rip), %rdi ; "0123456789012345" // 字符串的长度 movl $0x10, %esi callq 0x100012180 ; symbol stub for: Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String // rax: 0xd000000000000010 movq %rax, -0x10(%rbp) // rdx: 0x80000001000148a0 movq %rdx, -0x8(%rbp) libdyld.dylib`dyld_stub_binder: libswiftCore.dylib`Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String: // 比较字符串的长度和0xf的大小,如果小于 15,就采用 small string 的方式,否则就按普通的字符串存储 cmpq $0xf, %rsi jle 0x7fff6d40ecab // rdx = 0x7fffffffffffffe0 // rdx = 0x8000000000000020 movabsq $0x7fffffffffffffe0, %rdx // rdx = rdx + rdi addq %rdx, %rdi 复制代码
ASCII码表
libdyld.dylib`dyld_stub_binder
- 符号的延迟绑定通过 dyld_stub_binder 完成
- jmpq *0x1bc9(%rip) 占用六个字节
var str1 = "0123456789" var str2 = "0123456789" -----------------汇编分析-------------------- 第一次: jmpq *0x4ed2(%rip) ; (void *)0x00000001000123a0 jmpq *0x3e35(%rip) ; (void *)0x00007fff6df9c12c: dyld_stub_binder 然后调用 dyld_stub_binder 中就可以进行绑定了 第二次: jmpq *0x4ed2(%rip) ; (void *)0x00007fff6d40ec40: Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String 复制代码
因为我们是调用系统库的函数,我们自己并没有实现它,所以需要用这个函数帮我们把这个函数和系统的绑定到一起,这个是懒加载,也就是说当用到了之后他才会去绑定,而且绑定一次之后也会缓存起来,下次就不用再次绑定了,这个里面的汇编基本可以跳过。
总结
- 拼接前的字符串
- 字符串长度小于等于 0xF,字符串内容直接存放在 str1 变量的内存中
- 字符串长度大于 0xF,字符串内容存放在 __TEXT.cstring 中(常量区)
- 因为这个是编译的时候就确定的,所以不管有多长都会存放在这个区域
- 字符串的地址值信息存放在 str2 变量的后8个字节,不过需要经过一些计算就可以得到真正存储的地址了
- 后8个字节 + 0x0000000000000020 就可以拿到真正的地址了
- 拼接后的字符串
- 接拼后的字符串长度小于等于 0xF,所以字符串内容依然存放在 str1 变量的内存中
- 如果拼接后的字符串长充大于 0xF 中,开辟堆开间
思考
-
如何证明 rdi 存储的地址就放在全局区呢?
用 MachOView
-
dyld_stub_binder 的懒加载是怎么实现的?
第一次调用 // 0x100017038 jmpq *0x4ed2(%rip) ; (void *)0x00000001000123a0 复制代码可以看出来上面的函数地址和我们平时自己写的函数地址不是存在同一个区域的,我们平时都是在 __TEXT 区,他是存在 __DATA 区域的,也就是全局区,是允许我们在程序运行过程中动态修改的,当我们绑定完一次 dyld_stub_binder 后,程序会帮我们把这块内存修改了能够真正调用的地址了。
第二次调用 jmpq *0x4ed2(%rip) ; (void *)0x00007fff6d40ec40: 复制代码
关于 Array 的思考
- 1个 Array 变量占多少内存?
var a = [1, 2, 3, 4] printMemory(t1: a) print(malloc_size(UnsafeRawPointer(bitPattern: unsafeBitCast(a, to: Int.self)))) -----------------------执行结果----------------------- 8 8 8 64 复制代码
占8个字节 2. 数组中的数据存放在哪里?
-----------------------分析----------------------- (lldb) x/5wg 0x7ffeefbff438 0x7ffeefbff438: 0x000000010072f4a0 0x00007ffeefbff460 0x7ffeefbff448: 0x0000000100000c34 0x00007ffeefbff480 0x7ffeefbff458: 0x000000010004c025 (lldb) x/10wg 0x000000010072f4a0 0x10072f4a0: 0x00007fff97d19260 0x0000000000000002 0x10072f4b0: 0x0000000000000004 0x0000000000000008 0x10072f4c0: 0x0000000000000001 0x0000000000000002 0x10072f4d0: 0x0000000000000003 0x0000000000000004 0x10072f4e0: 0x0000000000000000 0x0000000000000000 复制代码
思考
- Array 底层更接近于 Class,为什么用结构体来实现?
- 数组的容量是什么时机扩容的?
var a = [Int]() (0...15).forEach { (i) in a.append(i) } (lldb) x/5wg 0x000000010057f090 0x10057f090: 0x00007fff97d19f80 0x0000000000000002 0x10057f0a0: 0x0000000000000010 0x0000000000000020 var a = [Int]() (0...16).forEach { (i) in a.append(i) } (lldb) x/5wg 0x000000010057f090 0x10057f090: 0x00007fff97d19f80 0x0000000000000002 0x10057f0a0: 0x0000000000000011 0x0000000000000040 var a = [Int]() (0...64).forEach { (i) in a.append(i) } (lldb) x/100wg 0x0000000104000000 0x104000000: 0x00007fff97d19f80 0x0000000000000002 0x104000010: 0x0000000000000041 0x0000000000000178 0x104000020: 0x0000000000000000 0x0000000000000001 0x104000030: 0x0000000000000002 0x0000000000000003 0x104000040: 0x0000000000000004 0x0000000000000005 0x104000050: 0x0000000000000006 0x0000000000000007 0x104000060: 0x0000000000000008 0x0000000000000009 0x104000070: 0x000000000000000a 0x000000000000000b 0x104000080: 0x000000000000000c 0x000000000000000d 0x104000090: 0x000000000000000e 0x000000000000000f 0x1040000a0: 0x0000000000000010 0x0000000000000011 0x1040000b0: 0x0000000000000012 0x0000000000000013 0x1040000c0: 0x0000000000000014 0x0000000000000015 0x1040000d0: 0x0000000000000016 0x0000000000000017 0x1040000e0: 0x0000000000000018 0x0000000000000019 0x1040000f0: 0x000000000000001a 0x000000000000001b 0x104000100: 0x000000000000001c 0x000000000000001d 0x104000110: 0x000000000000001e 0x000000000000001f 0x104000120: 0x0000000000000020 0x0000000000000021 0x104000130: 0x0000000000000022 0x0000000000000023 0x104000140: 0x0000000000000024 0x0000000000000025 0x104000150: 0x0000000000000026 0x0000000000000027 0x104000160: 0x0000000000000028 0x0000000000000029 0x104000170: 0x000000000000002a 0x000000000000002b 0x104000180: 0x000000000000002c 0x000000000000002d 0x104000190: 0x000000000000002e 0x000000000000002f 0x1040001a0: 0x0000000000000030 0x0000000000000031 0x1040001b0: 0x0000000000000032 0x0000000000000033 0x1040001c0: 0x0000000000000034 0x0000000000000035 0x1040001d0: 0x0000000000000036 0x0000000000000037 0x1040001e0: 0x0000000000000038 0x0000000000000039 0x1040001f0: 0x000000000000003a 0x000000000000003b 0x104000200: 0x000000000000003c 0x000000000000003d 0x104000210: 0x000000000000003e 0x000000000000003f 0x104000220: 0x0000000000000040 0x00007fff8eeca970 0x104000230: 0x00007fff8eeca988 0x00007fff8eeca9a0 0x104000240: 0x00007fff8eeca9b8 0x00007fff8eeca9d0 复制代码
感觉像是预设了几个数组的大小,当你的容量达到一组上限的一半的时候,就会扩容,使用另一个更大的数字
Array 拼接后会发生什么
这篇关于站在汇编角度深入了解 Swift(十三)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-10-05Swift语法学习--基于协议进行网络请求
- 2022-08-17Apple开发_Swift语言地标注释
- 2022-07-24Swift 初见
- 2022-05-22SwiftUI App 支持多语种 All In One
- 2022-05-10SwiftUI 组件参数简写 All In One
- 2022-04-14SwiftUI 学习笔记
- 2022-02-23Swift 文件夹和文件操作
- 2022-02-17Swift中使用KVO
- 2022-02-08Swift 汇编 String array
- 2022-01-30SwiftUI3.0页面反向传值