block的一些原理
不包含局部变量和包含全局变量
当闭包內未包含局部变量时,其实跟函数一致,方法调用也是跟函数似的,拿到函数地址直接调用,函数存在于代码段。当闭包只包含全局变量情况也类似,可以直接调用。
包含局部变量
以只含一个局部变量为例:
当闭包包含局部变量时,需要在内存开辟一段堆空间。会申请24个字节,因内存对齐实际开辟32个字节。堆空间布局和类非常类似。前8个字节存放函数地址,(中间8个字节存放引用计数),后8个字节存放copy局部变量的值。
当调用闭包时,会取前8个字节也就是函数地址进行调用,这时内部默认会追加传入一个参数给函数(即闭包的地址)。当函数使用到局部变量时就会以闭包地址加上16个字节(即copy的局部变量值)来进行使用。使用完毕再去更新堆上的变量值。
字符串
数组
String, Array 定义和行为上是值类型,底层数据结构更接近引用类型。
结构体
结构体数据直接放在结构体内存里面。
枚举
枚举关联值直接保存在内存中,case在内存中只占用一个字节,取rawValue时类似于方法调用,值不保存在枚举对象中。
.self .type
class.self 指向类信息,和一个类实例对象前8个字节指向相同 class.type .self的类型 类的类型
Self和self的区别
.self可以用在类型后面取得类型本身,也可以用在实例后面取得这个实例本身
1.Self可以用于协议(protocol)中限制相关的类型
2.Self可以用于类(Class)中来充当方法的返回值类型
关于函数表派发
当创建对象成功后,对象的前8个字节存储的是类的类型信息地址,类的类型中会维护一个函数表,记录着函数地址。根据内存偏移可以查表。
对于如下代码:
class Person {
func go() { }
}
复制代码
在调用时汇编代码如下:
//rax存储着Person内存地址
0x1099f05f3 <+51>: movq %rax, -0x18(%rbp)
//取rax指向地址的前8个字节到rcx,即Person类型地址
0x1099f05f7 <+55>: movq (%rax), %rcx
//取rcx + 0x50地址到rcx,说明函数地址存储在类型信息偏移50个字节处
0x1099f05fa <+58>: movq 0x50(%rcx), %rcx
0x1099f05fe <+62>: movq %rax, %r13
0x1099f0601 <+65>: movq %rax, -0x28(%rbp)
//调用rcx
0x1099f0605 <+69>: callq *%rcx
复制代码
验证如下:
(lldb) register read rax
rax = 0x00006000038d0520
(lldb) x/gx 0x00006000038d0520
0x6000038d0520: 0x00000001099fbed8
(lldb) dis -s 0x00000001099fbed8
SwiftDemo`type metadata for SwiftDemo.Person:
0x1099fbed8 <+0>: movabsb 0x6000000001099fbe, %al
0x1099fbee1 <+9>: xchgl %esp, %eax
0x1099fbee2 <+10>: xorb 0x7fff(%rdi), %al
0x1099fbee8 <+16>: lock
0x1099fbee9 <+17>: cmpb $0x19, %al
0x1099fbeeb <+19>: andb %bh, %bh
0x1099fbeed <+21>: jg 0x1099fbeef ; type metadata for SwiftDemo.Person + 23
0x1099fbeef <+23>: addb %al, (%rax)
0x1099fbef1 <+25>: addb %al, (%rax)
0x1099fbef3 <+27>: addb %bl, (%rax)
0x1099fbef5 <+29>: addb $0x0, (%rax)
(lldb) x/gx 0x1099FBF28
0x1099fbf28: 0x00000001099efe60
(lldb) dis -s 0x00000001099efe60
SwiftDemo`Person.go():
0x1099efe60 <+0>: pushq %rbp
0x1099efe61 <+1>: movq %rsp, %rbp
0x1099efe64 <+4>: movq $0x0, -0x8(%rbp)
0x1099efe6c <+12>: movq %r13, -0x8(%rbp)
0x1099efe70 <+16>: popq %rbp
0x1099efe71 <+17>: retq
0x1099efe72 <+18>: nopw %cs:(%rax,%rax)
0x1099efe7c <+28>: nopl (%rax)
(lldb)
复制代码
函数表方法调用
对于走函数表派发的方法,函数表存在于类对象中,调用时会去类对象中根据具体偏移查找方法。类内部含有的成员变量会影响函数表的偏移,具体影响结果暂时未知。
Variadic Parameters
func arithmeticMean(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers
复制代码
断言
(lldb) expression -- self.view.backgroundColor = [UIColor redColor]
thread return可以接受一个表达式,调用命令之后直接从当前的frame返回表达式的值。 thread return [] (lldb) thread return NO
有时候我们想要了解线程堆栈信息,可以使用thread backtrace 或 bt thread backtrace [-c ] [-s ] [-e ] (lldb) bt
内联优化
内联优化(Inlining optimization):内联优化形象来说,就是在汇编中不使用 call func_name
语句,直接将外部方法内的语句“复制”到调用者的代码段内。这样做的好处是不用进行调用函数前的压栈、调用函数后的出栈操作,提高运行效率与栈空间利用率。
debugger Symbols
.dSYM文件是一个符号表文件, 这里面包含了一个16进制的保存函数地址映射信息的中转文件, 所有Debug的symbols都在这个文件中(包括文件名、函数名、行号等). 一般Xcode项目每次编译后, 都会产生一个新的.dSYM文件和.app文件, 这两者有一个共同的UUID.
一些英文后缀
ify---为后缀, 使成,使……化
这和ize类似
ize---为后缀,做成,变成,……化
serial --> serialize:序列化
simple-->simplify 简单化,简化
string --> stringify :字符串化
复制代码