swift 一些笔记

1,072 阅读5分钟

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)中来充当方法的返回值类型

swift 中 Self 与self

关于函数表派发

当创建对象成功后,对象的前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

LLDB调试技巧

内联优化

内联优化(Inlining optimization):内联优化形象来说,就是在汇编中不使用 call func_name 语句,直接将外部方法内的语句“复制”到调用者的代码段内。这样做的好处是不用进行调用函数前的压栈、调用函数后的出栈操作,提高运行效率与栈空间利用率。

debugger Symbols

.dSYM文件是一个符号表文件, 这里面包含了一个16进制的保存函数地址映射信息的中转文件, 所有Debug的symbols都在这个文件中(包括文件名、函数名、行号等). 一般Xcode项目每次编译后, 都会产生一个新的.dSYM文件和.app文件, 这两者有一个共同的UUID.

一些英文后缀

ify---为后缀, 使成,使……化
这和ize类似
ize---为后缀,做成,变成,……化
serial --> serialize:序列化
simple-->simplify 简单化,简化
string --> stringify :字符串化