屬性將值跟特定的類、結構或枚舉關聯。存儲屬性存儲常量或變量作為實例的一部分,而計算屬性計算(不是存儲)一個值。計算屬性可以用于類、結構體和枚舉,存儲屬性只能用于類和結構體。
常量結構體的存儲屬性
如果創建了一個結構體的實例并將其賦值給一個常量,則無法修改該實例的任何屬性,即使定義了變量存儲屬性
這種行為是由于結構體(struct)屬于值類型。當值類型的實例被聲明為常量的時候,它的所有屬性也就成了常量。
屬于引用類型的類(class)則不一樣。把一個引用類型的實例賦給一個常量后,仍然可以修改該實例的變量屬性。
延遲存儲屬性
延遲存儲屬性是指當第一次被調用的時候才會計算其初始值的屬性。在屬性聲明前使用lazy來標示一個延遲存儲屬性。
ps: 必須將延遲存儲屬性聲明成變量(使用var關鍵字),因為屬性的初始值可能在實例構造完成之后才會得到。而常量屬性在構造過程完成之前必須要有初始值,因此無法聲明成延遲屬性。
延遲屬性很有用,當屬性的值依賴于在實例的構造過程結束后才會知道具體值的外部因素時,或者當獲得屬性的初始值需要復雜或大量計算時,可以只在需要的時候計算它。
ps: 如果一個被標記為lazy的屬性在沒有初始化時就同時被多個線程訪問,則無法保證該屬性只會被初始化一次。
計算屬性
除存儲屬性外,類、結構體和枚舉可以定義計算屬性。計算屬性不直接存儲值,而是提供一個 getter 和一個可選的 setter,來間接獲取和設置其他屬性或變量的值。
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// 輸出 "square.origin is now at (10.0, 10.0)”
只有 getter 沒有 setter 的計算屬性就是只讀計算屬性。只讀計算屬性總是返回一個值,可以通過點運算符訪問,但不能設置新的值。
ps: 必須使用
var
關鍵字定義計算屬性,包括只讀計算屬性,因為它們的值不是固定的。let
關鍵字只用來聲明常量屬性,表示初始化后再也無法修改的值。
只讀計算屬性的聲明可以去掉get關鍵字和花括號.
屬性觀察器
屬性觀察器監控和響應屬性值的變化,每次屬性被設置值的時候都會調用屬性觀察器,甚至新值和當前值相同的時候也不例外。
可以為除了延遲存儲屬性之外的其他存儲屬性添加屬性觀察器,也可以通過重寫屬性的方式為繼承的屬性(包括存儲屬性和計算屬性)添加屬性觀察器。
ps: 不需要為非重寫的計算屬性添加屬性觀察器,因為可以通過它的 setter 直接監控和響應值的變化。
可以為屬性添加如下的一個或全部觀察器:
-
willSet
在新的值被設置之前調用 -
didSet
在新的值被設置之后立即調用
ps: 如果在一個屬性的didSet觀察器里為它賦值,這個值會替換之前設置的值。
全局變量和局部變量
計算屬性和屬性觀察器所描述的功能也可以用于全局變量和局部變量。全局變量是在函數、方法、閉包或任何類型之外定義的變量。局部變量是在函數、方法或閉包內部定義的變量。
在全局或局部范圍都可以定義計算型變量和為存儲型變量定義觀察器。計算型變量跟計算屬性一樣,返回一個計算結果而不是存儲值,聲明格式也完全一樣。
ps: 全局的常量或變量都是延遲計算的,跟延遲存儲屬性相似,不同的地方在于,全局的常量或變量不需要標記
lazy
修飾符。
局部范圍的常量或變量從不延遲計算。
類型屬性
ps: 跟實例的存儲型屬性不同,必須給存儲型類型屬性指定默認值,因為類型本身沒有構造器,也就無法在初始化過程中使用構造器給類型屬性賦值。
存儲型類型屬性是延遲初始化的,它們只有在第一次被訪問的時候才會被初始化。即使它們被多個線程同時訪問,系統也保證只會對其進行一次初始化,并且不需要對其使用lazy
修飾符。
使用關鍵字static來定義類型屬性。在為類定義計算型類型屬性時,可以改用關鍵字class來支持子類對父類的實現進行重寫。
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 6
}
}
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 27
}
class var overrideableComputedTypeProperty: Int {
return 107
}
}
跟實例屬性一樣,類型屬性也是通過點運算符來訪問。但是,類型屬性是通過類型本身來訪問,而不是通過實例.
print(SomeStructure.storedTypeProperty)
// 輸出 "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// 輸出 "Another value.”
print(SomeEnumeration.computedTypeProperty)
// 輸出 "6"
print(SomeClass.computedTypeProperty)
// 輸出 "27"
要點總結
全局變量和局部變量也可以使用計算屬性和屬性觀察器所描述的功能。
The capabilities of computing and observing properties are also available to global variables and local variables.