一、存储属性
- 存储属性是一个作为特定类和结构体实例一部分的常量或变量。存储属性要么是变量存储属性 (由 var 关键字引入)要么是常量存储属性(由 let 关键字引入)。
区分var和let
- 汇编角度分析:
var age = 10
var name = "Lee"
将0x12复制到寄存器x10;
将0x14复制到寄存器x8,
汇编角度看起来没区别;
-SIL角度分析:
从上述SIL代码可以看出,let和get都是存储属性,都有初始值。let只有get方法,没有set方法。本质上let和var也是一种语法糖
二、计算属性
存储的属性是最常⻅的,除了存储属性,类、结构体和枚举也能够定义计算属性,计算属性并不 存储值,他们提供 getter 和 setter 来修改和获取值。对于存储属性来说可以是常量或变量,但计算属性必须定义为变量。于此同时我们书写计算属性时候必须包含类型,因为编译器需 要知道期望返回值是什么。
struct Square {
var width: Double
var area: Double {
get {
return width * width
}
set {
self.width = newValue
}
}
}
class Square {
var width: Double = 8.0
var area: Double {
get {
return width * width
}
set (newValue) {
width = sqrt(newValue)
}
}
}
let s = Square()
print(class_getInstanceSize(Square.self)) //输出24
print(s.area)
通过sil查看:
struct Square {
//width有存储属性,实例中占据内存,占8字节
@_hasStorage var width: Double { get set }
// ,不占据内存
var area: Double { get set }
init(width: Double)
}
class Square {
// width有存储属性,实例中占据内存,占8字节
@_hasStorage @_hasInitialValue var width: Double { get set }
// area没有存储属性,不占据内存
var area: Double { get set }
@objc deinit
init()
}
再比如:private(set) var area: Double = 30写法,含义将set方法私有。内部只读,外部不允许访问。
此时查看SIL文件:
总结:
计算属性本质get、set方法。
三、属性观察者
属性观察者会观察用来观察属性值的变化,一个 willSet 当属性将被改变调用,即使这个值与原有的值相同,而 didSet 在属性已经改变之后调用。它们的语法类似于 getter 和 setter。
class SubjectName {
var subjectName: String = "" {
willSet {
print("subjectName will set value \(newValue)")
}
didSet {
print("subjectName has been changed \(oldValue)")
}
}
init(subjectName: String) {
self.subjectName = subjectName
}
}
let s = SubjectName(subjectName: "swift进阶")
s.subjectName = "swift"
// 输出结果
subjectName will set value swift
subjectName has been changed swift进阶
总结
:willSet:新值存储之前调用 newValue;didSet:新值存储之后调用 oldValue
- 这里我们在使用属性观察器的时候,需要注意的一点是在初始化期间设置属性时不会调用 willSet 和 didSet 观察者;只有在为完全初始化的实例分配新值时才会调用它们。运行下面这段代码,你会发现当前并不会有任何的输出。
计算属性观察者
class SubjectName{
var subjectName: String = "[unnamed]"{
willSet{
print("subjectName will set value \(newValue)")
} didSet{
print("subjectName has been changed \(oldValue)") }
}
init(subjectName: String) {
self.subjectName = subjectName
}
}
let s = SubjectName(subjectName: "Swift进阶")
可以看出
init方法没有调用willset和didset方法,而是直接把subjectName拷贝到内存中。
继承属性观察者
class Teacher {
var age: Int {
willSet{
print("age will set value \(newValue)")
} didSet{
print("age has been changed \(oldValue)")
}
}
var height: Double
init(_ age: Int, _ height: Double) {
self.age = age
self.height = height
}
}
class ParTimeTeacher: Teacher {
override var age: Int {
willSet{
print("override age will set value \(newValue)")
} didSet{
print("override age has been changed \(oldValue)")
}
}
var subjectName: String
init(_ subjectName: String) {
self.subjectName = subjectName
super.init(10, 180)
self.age = 20
}
}
var t = ParTimeTeacher("Swift")
//运行结果
//override age will set value 20
//age will set value 20
//age has been changed 10
//override age has been changed 10
当在继承的时候,属性的观察者的调用方式是
- 调用子类的的willset
- 调用父类的willset
- 调用父类的didset
- 调用子类的didset
延迟存储属性
用关键字 lazy 来标识一个延迟存储属性,延迟存储属性必须有初始值
lazy属性必须是var,不能是let,因为let必须在实例的初始化方法完成之前就拥有值。
class sub { lazy var age: Int = 18 }
swift 内存独占
必须保证init方法的私有性,只有这样,才能保证单例是真正唯一的,避免外部对象通过访问init方法创建单例类的其他实例。
class LeeTeacher() {
static let shareInstance = LeeTeacher()
private init() {}
}
12