屬性
本節(jié)內(nèi)容包括:
- 存儲屬性
- 計算屬性
- 屬性觀察器
- 全局變量和局部變量
- 類型屬性
存儲屬性
簡單來說届案,一個存儲屬性就是存儲在特定類或結(jié)構(gòu)體的實例里的一個常量或變量秸仙,存儲屬性可以是變量存儲屬性(用關(guān)鍵字var定義)鸟缕,也可以是常量存儲屬性(用關(guān)鍵字let定義)瘫筐。
struct FixedLengthRange {
var firstValue: Int
let length: Int
}
常量和存儲屬性
如果創(chuàng)建了一個結(jié)構(gòu)體的實例并賦值給一個常量矩桂,則無法修改實例的任何屬性软族,即使定義了變量存儲屬性:
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// 該區(qū)間表示整數(shù)0添谊,1财喳,2,3
rangeOfFourItems.firstValue = 6
// 盡管 firstValue 是個變量屬性碉钠,這里還是會報錯
因為rangeOfFourItems
聲明成了常量(用let關(guān)鍵字)纲缓,即使firstValue
是一個變量屬性,也無法再修改它了喊废。
這種行為是由于結(jié)構(gòu)體(struct)屬于值類型祝高。當(dāng)值類型的實例被聲明為常量的時候,它的所有屬性也就成了常量污筷。
屬于引用類型的類(class)則不一樣工闺,把一個引用類型的實例賦給一個常量后乍赫,仍然可以修改實例的變量屬性。
延遲存儲屬性
延遲存儲屬性是指當(dāng)?shù)谝淮伪徽{(diào)用的時候才會計算其初始值的屬性陆蟆。在屬性聲明前使用lazy
來標(biāo)示一個延遲存儲屬性雷厂。
注意:
必須將延遲存儲屬性聲明成變量(使用var關(guān)鍵字),因為屬性的值在實例構(gòu)造完成之前可能無法得到叠殷。而常量屬性在構(gòu)造過程完成之前必須要有初始值改鲫,因此無法聲明成延遲屬性。
存儲屬性和實例變量
Swift 中的屬性沒有對應(yīng)的實例變量林束,屬性的后端存儲也無法直接訪問像棘。這就避免了不同場景下訪問方式的困擾,同時也將屬性的定義簡化成一個語句壶冒。 一個類型中屬性的全部信息——包括命名缕题、類型和內(nèi)存管理特征——都在唯一一個地方(類型定義中)定義。
計算屬性
除存儲屬性外胖腾,類烟零、結(jié)構(gòu)體和枚舉可以定義計算屬性,計算屬性不直接存儲值咸作,而是提供一個 getter 來獲取值锨阿,一個可選的 setter 來間接設(shè)置其他屬性或變量的值。
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(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.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)
println("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// 輸出 "square.origin is now at (10.0, 10.0)”
square
的center
屬性可以通過點運算符(square.center
)來訪問性宏,這會調(diào)用 getter
來獲取屬性的值群井。跟直接返回已經(jīng)存在的值不同,getter
實際上通過計算然后返回一個新的Point來表示square
的中心點毫胜。如代碼所示书斜,它正確返回了中心點(5, 5)。
center
屬性之后被設(shè)置了一個新的值(15, 15)酵使,表示向右上方移動正方形到如圖所示橙色正方形的位置荐吉。設(shè)置屬性center
的值會調(diào)用 setter
來修改屬性origin
的x
和y
的值,從而實現(xiàn)移動正方形到新的位置口渔。
只讀計算屬性
只有 getter 沒有 setter 的計算屬性就是只讀計算屬性样屠。只讀計算屬性總是返回一個值,可以通過點運算符訪問缺脉,但不能設(shè)置新的值痪欲。
注意:
必須使用var關(guān)鍵字定義計算屬性,包括只讀計算屬性攻礼,因為它們的值不是固定的业踢。let關(guān)鍵字只用來聲明常量屬性,表示初始化后再也無法修改的值礁扮。
屬性觀察器
屬性觀察器監(jiān)控和響應(yīng)屬性值的變化知举,每次屬性被設(shè)置值的時候都會調(diào)用屬性觀察器瞬沦,甚至新的值和現(xiàn)在的值相同的時候也不例外。
可以為除了延遲存儲屬性之外的其他存儲屬性添加屬性觀察器雇锡,也可以通過重載屬性的方式為繼承的屬性(包括存儲屬性和計算屬性)添加屬性觀察器逛钻。
注意:
不需要為無法重載的計算屬性添加屬性觀察器,因為可以通過 setter 直接監(jiān)控和響應(yīng)值的變化锰提。
可以為屬性添加如下的一個或全部觀察器:
-
willSet
在設(shè)置新的值之前調(diào)用 -
didSet
在新的值被設(shè)置之后立即調(diào)用
注意:
willSet和didSet觀察器在屬性初始化過程中不會被調(diào)用曙痘,它們只會當(dāng)屬性的值在初始化之外的地方被設(shè)置時被調(diào)用。
如果在didSet觀察器里為屬性賦值欲账,這個值會替換觀察器之前設(shè)置的值屡江。
全局變量和局部變量
計算屬性和屬性觀察器所描述的模式也可以用于全局變量和局部變量,全局變量是在函數(shù)赛不、方法、閉包或任何類型之外定義的變量罢洲,局部變量是在函數(shù)踢故、方法或閉包內(nèi)部定義的變量。
前面章節(jié)提到的全局或局部變量都屬于存儲型變量惹苗,跟存儲屬性類似殿较,它提供特定類型的存儲空間,并允許讀取和寫入桩蓉。
另外淋纲,在全局或局部范圍都可以定義計算型變量和為存儲型變量定義觀察器,計算型變量跟計算屬性一樣院究,返回一個計算的值而不是存儲值洽瞬,聲明格式也完全一樣。
注意:
全局的常量或變量都是延遲計算的业汰,跟延遲存儲屬性相似伙窃,不同的地方在于,全局的常量或變量不需要標(biāo)記lazy特性样漆。
局部范圍的常量或變量不會延遲計算为障。
類型屬性
實例的屬性屬于一個特定類型實例,每次類型實例化后都擁有自己的一套屬性值放祟,實例之間的屬性相互獨立鳍怨。
也可以為類型本身定義屬性,不管類型有多少個實例跪妥,這些屬性都只有唯一一份鞋喇。這種屬性就是類型屬性。
類型屬性用于定義特定類型所有實例共享的數(shù)據(jù)骗奖,比如所有實例都能用的一個常量(就像 C 語言中的靜態(tài)常量)确徙,或者所有實例都能訪問的一個變量(就像 C 語言中的靜態(tài)變量)醒串。
對于值類型(指結(jié)構(gòu)體和枚舉)可以定義存儲型和計算型類型屬性,對于類(class)則只能定義計算型類型屬性鄙皇。
值類型的存儲型類型屬性可以是變量或常量芜赌,計算型類型屬性跟實例的計算屬性一樣定義成變量屬性。
注意:
跟實例的存儲屬性不同伴逸,必須給存儲型類型屬性指定默認(rèn)值缠沈,因為類型本身無法在初始化過程中使用構(gòu)造器給類型屬性賦值。
類型屬性語法
在 Swift 編程語言中错蝴,類型屬性是作為類型定義的一部分寫在類型最外層的花括號內(nèi)洲愤,因此它的作用范圍也就在類型支持的范圍內(nèi)。
使用關(guān)鍵字static來定義值類型的類型屬性顷锰,關(guān)鍵字class來為類(class)定義類型屬性柬赐。
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
// 這里返回一個 Int 值
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
// 這里返回一個 Int 值
}
}
class SomeClass {
class var computedTypeProperty: Int {
// 這里返回一個 Int 值
}
}
獲取和設(shè)置類型屬性的值
跟實例的屬性一樣,類型屬性的訪問也是通過點運算符來進行官紫,但是肛宋,類型屬性是通過類型本身來獲取和設(shè)置,而不是通過實例束世。
println(SomeClass.computedTypeProperty)
// 輸出 "42"
println(SomeStructure.storedTypeProperty)
// 輸出 "Some value."
SomeStructure.storedTypeProperty = "Another value."
println(SomeStructure.storedTypeProperty)
// 輸出 "Another value.”