结构体和类的相同点:
- 定义存储值的属性
- 定义方法
- 定义下标语法提供对其值的访问
- 定义初始化器
- 使用extension来扩展功能
- 遵循协议来提供某种功能
结构体与类的不同点:
- 类有继承的属性,而结构体没有
- 类型转换使您能够运行时检查和解释类实例的类型
- 类有析构函数用来释放其分配的资源
- 引用计数允许对一个类实例有多个引用
一.对于类和结构体最主要的区别是类是内存地址引用,结构体是值copy
import Foundation
class Teacher{
var age: Int
var name: String
init(_ age: Int, _ name: String) {
self.age = age
self.name = name
}
}
var t = Teacher(1,"lisi")
var t1 = t
print("end")
- 说明class实例对象的赋值是引用类型的赋值
- 说明class对象本身的地址是在栈开辟,并且t和t1相差8个字节
值类型存储的是具体的实例,比较典型的例子就是struct,稍加修改上面的例子
t和t1都是直接存储的值,t和t1之间是相互独立的关系,一般值类型都存储在栈上,引用类型存储在堆上,数据具体存储位置分布图如下:
如果没有其他额外的需求,只是用作存储数据,那么我们应该优先选择结构体,因为结构体在大量的创建性能优化上是比class优异的多,因为class需要去维护堆内存的创建,销毁,寻找,引用关系的创建等等,十分消耗性能
二.类的初始化
import Foundation
struct Teacher{
var age: Int
var name: String
// init(_ age: Int, _ name: String) {
// self.age = age
// self.name = name
// }
}
var t = Teacher(age: 1, name: "lishi")
var t1 = t
print("end")
结构体是默认就初始化函数的,class如果没有设置初始化函数就会报错,所以class里面必须需要有咱们自己的初始化器,同时我们的便捷初始化器里面必须调用另一个初始化器,如果不这样做就会报错
import Foundation
class Teacher{
var age: Int
var name: String
init(_ age: Int, _ name: String) {
self.age = age
self.name = name
}
convenience init(age: Int, name: String) {
self.init(age, name)
}
}
var t = Teacher(1,"lisi")
var t1 = t
print("end")
这里我们记住:
- 指定初始化器必须保证在向上委托给父类初始化器之前,其所在类引入的所有属性都要初始化完成
- 指定初始化器必须先向上委托父类初始化器,然后才能为继承的属性设置新值。如果不这样做,指定初始化器赋予的新值将被父类中的初始化器所覆盖
- 便捷初始化器必须先委托同类中的其它初始化器,然后再为任意属性赋新值(包括同类里定义的属性)。如果没这么做,便捷构初始化器赋予的新值将被自己类中其它指定初始化器所覆盖
- 初始化器在第一阶段初始化完成之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用 self 作为值
可失败初始化器: 这个也非常好理解,也就意味着当前因为参数的不合法或者外部条件的不满足,存在初始化失败的情况。这种 Swift 中可失败初始化器写 return nil 语句,来表明可失败初始化器在何种情况下会触发初始化失败。写法也非常简单:
import Foundation
class Teacher{
var age: Int
var name: String
init?(_ age: Int, _ name: String) {
if age < 18 {
return nil
}
self.age = age
self.name = name
}
convenience init?(age: Int, name: String) {
self.init(age, name)
}
}
var t = Teacher(18,"lisi")
var t1 = t
print("end")
必要初始化器:在类的初始化器前添加 required 修饰符来表明所有该类的子类都必须实现该初始化器,不然就会报错
import Foundation
class Teacher{
var age: Int
var name: String
required init(_ age: Int, _ name: String) {
self.age = age
self.name = name
}
}
class sonTeacher : Teacher{
var subName: String
init(_ subName: String) {
self.subName = subName
super.init(22, subName)
}
required init(_ age: Int, _ name: String) {
self.subName = "subName"
super.init(12, "name")
}
}
var t = sonTeacher("222")
var t1 = t
print("end")
三.类的生命周期
iOS开发的语言不管是OC还是Swift后端都是通过LLVM进行编译的,如下图所示:
OC 通过 clang 编译器,编译成 IR,然后再生成可执行文件 .o(这里也就是我们的机器码) Swift 则是通过 Swift 编译器编译成 IR,然后在生成可执行文件。
Swift 对象内存分配
__allocating_init -----> swift_allocObject -----> swift_allocObject -----> swift_slowAlloc -----> Malloc
Swift 对象的内存结构 HeapObject (OC objc_object) ,有两个属性: 一个是Metadata ,一个是 RefCount ,默认占用 16 字节大小。
经过源码分析我们不难得出 swift 类的数据结构
struct Metadata{
var kind: Int
var superClass: Any.Type
var cacheData: (Int, Int)
var data: Int
var classFlags: Int32
var instanceAddressPoint: UInt32
var instanceSize: UInt32
var instanceAlignmentMask: UInt16
var reserved: UInt16
var classSize: UInt32
var classAddressPoint: UInt32
var typeDescriptor: UnsafeMutableRawPointer
var iVarDestroyer: UnsafeRawPointer
}
由上面可以看出swift的内存结构跟oc类似,oc中是通过对象的isa指针指向class类,class类中有属性列表,方法列表,协议列表等,class类的isa指向根元类
- oc更多是动态语法决议导致有了很多不确定因素,当然也有很多的容错可能,利弊共存 swift中对性能的追求更加的极致,在能用struct来优化性能的地方,系统都给我们做好了,最明显的例子就是UUID这个对象就是一个值对象
- 底层对内存的使用与优化都是同宗同源