类与结构体
类与结构体的相同点和区别
相同点:
- 定义存储属性;
- 定义方法;
- 定义初始化器;
- 定义下标以使用下标语法提供对其值的访问;
- 通过extension来扩展功能;
- 遵循协议来提供某种功能;
区别:
- 类是引用类型,结构体是值类型;
- 类有类型转换,可以在运行时检查和解释类实例的类型;
- 类可以继承,结构体不可以;
- 引用计数允许对一个实例有多个引用;
- 类有析构函数,用来释放其分配的资源;
调试过程常用命令:
//打印变量的内存地址
(lldb) po withUnsafePointer(to: &t){print($0)}
0x00007ff7b75c5ec8
0 elements
//查看内存地址的类型,需要配置一个脚本 (链接: https://pan.baidu.com/s/1tZU8_KtK9VtS9pTmgwQQIQ 提取码: murc )
(lldb) cat address 0x00007ff7b951ce90
address:0x00007ff7b951ce90, stack address (SP: 0x7ff7b951ce50 FP: 0x7ff7b951cec0) SwiftDemo.ViewController.test() -> ()
//输出对应的值
(lldb) po t
<LZTeacher: 0x6000012b08d0>
//输出对应的值,并返回值的类型以及命令结果的引用名
(lldb) p t
(SwiftDemo.LZTeacher) $R1 = 0x00006000012b08d0 (age = 19, name = "jim")
//读取内存中的值(8g:8字节格式输出)
(lldb) x/8g 0x00007ff7b951ce90
0x7ff7b951ce90: 0x0000000000000013 0x0000000000007373
0x7ff7b951cea0: 0xe200000000000000 0x0000600001f429a0
0x7ff7b951ceb0: 0x00007f848bf0e3b0 0x00007f848bf0e3b0
0x7ff7b951cec0: 0x00007ff7b951cef0 0x00000001069e1b54
//查看变量的内存地址
(lldb) frame variable -L t
scalar: (SwiftDemo.LZTeacher) t = 0x00006000012b08d0 {
0x00006000012b08e0: age = 19
0x00006000012b08e8: name = "jim"
}
存储属性
let 和 var的区别: 从汇编角度: 从SIL的角度:
类的初始化器
当前的类编译器默认不会自动提供成员初始化器,如果自己不定义初始化器的话会报错;
结构体的话,编译器会提供默认的初始化器(前提是我们自己没有指定初始化器)
class Person{
var age = 19
var name = "dd"
//初始化器,也就是构造函数
//指定初始化器,默认只有一个
init(_ age: Int, _ name: String) {
self.age = age
self.name = name
}
//其他的初始化器都定义为便捷初始化器
//便捷初始化器,必须从相同类里面调用另一个初始化器(安全考虑,保证所有的成员都已经被初始化),否则会报错
convenience init() {
self.init(19, "")//这个初始化器必须先调用,否则会报错
self.age = 16
self.name = "ff"
}
}
struct Person{
var age = 19
var name = "dd"
}
从Person类中派生出一个子类Teacher,并指定一个初始化器,如下所示:
class Teacher: Person{
var sex: String
init(_ sex: String){
self.sex = sex //指定初始化器必须保证在向上委托给父类初始化器之前,其所在类引入的所有属性都要初始化完成
super.init(19, "jj") //指定初始化器必须先向上委托父类初始化器,然后才能为继承的属性设置新值。如果不这样做,指定初始化器赋予的新值将被父类中的初始化器所覆盖
}
}
必要初始化器
在类的初始化器前添加 required 修饰符来表明所有该类的子类都必须实现该初始器。
class Person{
var age = 19
var name = "dd"
required init(_ age: Int, _ name: String) {
self.age = age
self.name = name
}
...
}
class Teacher: Person{
var sex: String
required init(_ age: Int, _ name: String) {
fatalError("init(_:_:) has not been implemented")
}
}
类生命周期
iOS 开发的语言不管是OC还是Swift后端都是通过LLVM进行编译的,如图所示:
Swift的编译过程:
如图所示的编译流程,对应的命令如下:
// 分析输出AST
swiftc main.swift -dump-parse
// 分析并且检查类型输出AST
swiftc main.swift -dump-ast
// 生成中间体语言(SIL),未优化
swiftc main.swift -emit-silgen
// 生成中间体语言(SIL),优化后的
swiftc main.swift -emit-sil
// 生成LLVM中间体语言 (.ll文件)
swiftc main.swift -emit-ir
// 生成LLVM中间体语言 (.bc文件)
swiftc main.swift -emit-bc
// 生成汇编
swiftc main.swift -emit-assembly
// 编译生成可执行.out文件
swiftc -o main.o main.swift
Swift对象内存分配
底层调用过程:
__allocating_init -----> swift_allocObject -----> _swift_allocObject_ -----> swift_slowAlloc -----> Malloc
Swift 对象的内存结构HeapObject(OC objc_object),有两个属性:一个是Metadata,一个是RefCount,默认占用16字节大小。
struct HeapObject {
HeapMetadata const *metadata;
}
Swift类的数据结构(看源码得出的结论):
生成sil中间文件的脚本,可以在xcode中配置,配置之后运行可以生成sil文件:
swiftc -emit-sil ${SRCROOT}/LGSwiftTest/main.swift > ./main.sil && open main.sil
SIL文件中一些语法解释:
@main : 入口函数,@作为当前的标识符
%0:寄存器,相当于常量
使用命令: xcrun swift-demangle xxx 可以翻译下面的符号值
@$s4main10HeapObjectV8metadataSVvs
sil的文档: github.com/apple/swift…
类的内存结构的探索
swift源码:github.com/apple/swift