一、 存儲屬性
存儲屬性,分兩種:let修飾的常量存儲屬性
;var修飾的變量存儲屬性
。
還用之前的代碼:
class Person {
var age: Int = 33
var name: String = "chen"
}
let t = Person()
特點:占用分配實例對象的內存空間,即,堆空間。
二、 計算屬性
類
、結構體
和枚舉
可以定義計算屬性,計算屬性不直接存儲值,而是提供一個getter
和一個可選的 setter
,來間接獲取和設置其他屬性或變量的值。
必須使用 var
關鍵字定義計算屬性,只有 getter 沒有 setter
的計算屬性叫只讀計算屬性
,不能設置新的值。
特點:不占用內存空間,本質是
set/get
方法。
以下代碼是否正確?
運行會崩潰,原因是
age
的set
方法中調用age.set
導致了循環引用,即遞歸
。代碼驗證計算屬性:
class Circle {
var radius: Double = 2;
var area: Double{
get{
return 3.14 * radius * radius
}
set{
radius = sqrt(newValue / 3.14)
}
}
}
print(class_getInstanceSize(Circle.self))
//打印結果:24
從結果可以看出,類Circle的內存大小是:24 = 類自帶16字節 + radius(8字節)
,是沒有加上area
的。也就是說, area屬性沒有占有內存空間
。
我們也可以通過SIL,驗證一下:
swiftc -emit-sil main.swift |xcrun swift-demangle >> ./main.sil && open main.sil
SIL如下:
class Circle {
@_hasStorage @_hasInitialValue var radius: Double { get set }
var area: Double { get set }
@objc deinit
init()
}
存儲屬性
,有_hasStorage
的標識符。計算屬性
只有get、set
方法。
三、 屬性觀察器
class Teacher {
var name: String = "chinese"{
//新值存儲之前調用
willSet{
print("willSet newValue \(newValue)")
}
//新值存儲之后調用
didSet{
print("didSet oldValue \(oldValue)")
}
}
}
let t: Teacher = Teacher();
t.name = "english"
//打印結果:
//willSet newValue english
//didSet oldValue chinese
特點:
1、在當前類的init
方法中,如果調用屬性,是不會
觸發屬性觀察者的(因為內存安全,不確定變量是否初始化結束);觸發get、set
2、在子類的init
方法中,如果調用屬性,會觸發屬性觀察者。也是因為內存安全,子類調用了父類的init,已經初始化過了,而初始化流程保證了所有屬性都有值(即super.init確保變量初始化完成了),所以可以觀察屬性了
3、對于同一個屬性,子類和父類
都有屬性觀察者,其順序是:先子類willset,后父類willset,在父類didset, 子類的didset,即:子父 父子
四、延遲屬性
延遲屬性,即,使用lazy修飾
的存儲屬性。特點:
- 必須有一個
默認
的初始值。- 第一次訪問的時候才被賦值
- 不能保證線程安全
- 影響實例對象內存的大小
class Teacher {
lazy var age: Int = 18
}
let t = Teacher()
print("-----Teacher-------")
print(t.age)
print(class_getInstanceSize(Teacher.self))
print("-----Int-------")
print(MemoryLayout<Int>.size)
print(MemoryLayout<Optional<Int>>.size)
print(MemoryLayout<Optional<Int>>.stride)
斷點調試:
查看內存,前面兩個內存段,分別是Metadata、refCounted,第三個應該是age屬性,但是此時內存值是0。
繼續打印:
-----Teacher-------
18
32
-----Int-------
8
9
16
此時t.age的值正常打印,查看源碼發現,lazy屬性默認是Optional類型的
,只有set
和get
方法,也就是默認值為nil
。第一次調用get方法時,才會被初始化。
關于內存,如果age
屬性沒有lazy
修飾, class_getInstanceSize(Teacher.self) = 24
;有lazy修飾
時,是32
,這個通過打印可以看到,Optional<Int>
內存占9字節
,內存對齊
,所以是32
。
五、 類型屬性
使用關鍵字static修飾
,且是一個全局變量
。特點
- 類型屬性必須有一個
默認的初始值
;- 類型屬性只會被
初始化一次
。
參考:Swift-屬性