简介:
上篇文章主要分析了Swift的SIL编译流程及Swift语言类的结构,这篇文章主要分析类的属性及属性观察者
类的属性
Swift类的属性大体分为两部分:存储属性,``计算属性,延迟存储属性以及类型属性
- 存储属性
简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量,存储属性可以是变量存储属性(用关键字var定义),也可以是常量存储属性(用关键字let定义)
class LGPerson {
let age: Int = 18
var name: String = "World"
}
let t = LGPerson()
其中代码中的age来说,都是常量存储属性,``name为变量存储属性
SIL验证:
进入终端输入:swiftc -emit-sil main.swift >> ./mainSwift.sil,利用VSCode打开mainSwift.sil查看类的和属性的定义
系统默认添加了构造函数和析构函数
查看属性的内存存储
分析**SIL结构**(只分析了**setter/getter**)
- 计算属性
除存储属性外,类、结构体和枚举可以定义计算属性,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值
class LGPerson {
var age: Int = 18
var area:Float {
get {
return Float(age)
}
}
var newPerson:Int {
get {
return age + 25
}
set {
age = newValue
}
}
}
SIL类结构
SIL的getter方法
计算方法
- 延迟属性
延迟属性主要有以下几点说明:
1、使用lazy修饰的存储属性
2、延迟属性必须有一个默认的初始值
3、延迟存储在第一次访问的时候才被赋值
4、延迟存储属性并不能保证线程安全
5、延迟存储属性对实例对象大小的影响
使用lazy修饰的存储属性
class LGPerson {
lazy var age = 18
}
延迟属性必须有一个默认的初始值
如果没有给默认值,系统会报错
延迟存储在第一次访问的时候才被赋值
通过**sil**文件来查看
getter方法
setter方法
延迟存储属性并不能保证线程安全
例如:
getter方法,同时有两个线程获取age的值线程1此时访问age,其age是没有值的,进入bb2流程, 然后时间片将CPU分配给了线程2,对于optional来说,依然是none,同样可以走到bb2流程,所以并不能保证属性只初始化了一次
延迟存储属性对实例对象大小的影响
- 类型属性
实例的属性属于一个特定类型实例,每次类型实例化后都拥有自己的一套属性值,实例之间的属性相互独立。也可以为类型本身定义属性,不管类型有多少个实例,这些属性都只有唯一一份。这种属性就是类型属性
-
类型属性用于定义特定类型所有实例共享的数据
-
对于值类型(指结构体和枚举)可以定义存储型和计算型类型属性,对于类(class)则只能定义计算型类型属性
-
值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样定义成变量属性。
使用关键字static修饰切是一个全局变量
class LGPerson {
static var age = 18
}
var t = LGPerson.age
查看**sil**文件中**类**的构成
查看**age**的**getter**方法
查看**function_ref LGPerson.age.unsafeMutableAddressor**
通过断点调试LGPerson.age.unsafeMutableAddressor,发现调用的是**swift_once**,表示属性只初始化一次
进入swift源码查找swift_once的实现
swift源码可看出swift_once的实现是一个单利模式,只执行一次
swift中单利的实现
class LGPerson {
// 创建单例对象
static let sharedInstance = LGPerson()
// 重写init方法,设为私有方法
private init(){}
}
类型属性必须有一个默认的初始值
属性观察者
属性观察者是监控和响应``属性值的变化,每次属性被设置值的时候都会调用属性观察者,甚至新的值和现在的值相同的时候也不例外.
class LGPerson {
var name: String = "world" {
willSet {
print("新值: \(newValue)")
print("willSet")
}
didSet {
print("didSet")
}
}
}
let p = LGPerson()
p.name = "好好"
**
- SIL源码探索:
**
可以看到**name**属性是**存储型属性**,具有**set**和**get**方法
查看name的setter方法
查看willset方法
查看**didSet**方法
属性的继承
属性一般都可继承,除非:
-
子类
不可重写let属性,但可访问 -
子类
不可重写和父类不一致的属性(计算型改成存储型) -
子类willSet->父类willSet->父类didSet->子类didSet -
如果
父类将所有属性都实现了,【子类init时】可正常触发【属性观察者】 -
类中的
init方法赋值不会触发属性观察
总结
-
存储属性会占用实例变量的内存空间,且计算属性不占用内存空间,计算属性本质上是setter/getter方法 -
延迟存储属性使用lazy修饰存储属性,且必须有一个默认值只有在第一次被访问时才会被赋值,且是线程不安全的 -
类型属性用static修饰,且必须有一个默认初始值,它一个全局变量,只会被初始化一次,是线程安全的