計(jì)算屬性和存儲(chǔ)屬性
我們知道在OC中, 使用@ property聲明的實(shí)例變量, 都會(huì)自動(dòng)生成setter和getter方法, 但是在swift中, 屬性分為計(jì)算屬性和存儲(chǔ)屬性, 存儲(chǔ)屬性存儲(chǔ)常量或變量作為實(shí)例的一部分萄喳,計(jì)算屬性計(jì)算(而不是存儲(chǔ))一個(gè)值,就是setter和getter方法, 除存儲(chǔ)屬性外加派,類素标、結(jié)構(gòu)體和枚舉可以定義計(jì)算屬性蛆楞,計(jì)算屬性不直接存儲(chǔ)值,而是提供一個(gè) getter 來(lái)獲取值,一個(gè)可選的 setter 來(lái)間接設(shè)置其他屬性或變量的值。
延遲存儲(chǔ)屬性
延遲存儲(chǔ)屬性是指當(dāng)?shù)谝淮伪徽{(diào)用的時(shí)候才會(huì)計(jì)算其初始值的屬性。在屬性聲明前使用lazy(xcode8)不是@lazy來(lái)標(biāo)示一個(gè)延遲存儲(chǔ)屬性羹应。類似于OC中的懶加載
注意:必須將延遲存儲(chǔ)屬性聲明成變量(使用var關(guān)鍵字),因?yàn)閷傩缘闹翟趯?shí)例構(gòu)造完成之前可能無(wú)法得到(意思就是說(shuō), 延遲屬性, 不需要在創(chuàng)建實(shí)例時(shí)賦初始值)次屠。而常量(使用var)屬性在構(gòu)造過(guò)程完成之前必須要有初始值(如果你不給初始值, 編譯器會(huì)根據(jù)類型推斷, 自動(dòng)分配)园匹,因此無(wú)法聲明成延遲屬性。
class DataImporter {
/*
DataImporter 是一個(gè)將外部文件中的數(shù)據(jù)導(dǎo)入的類劫灶。
這個(gè)類的初始化會(huì)消耗不少時(shí)間裸违。
*/
var fileName = "data.txt"
// 這是提供數(shù)據(jù)導(dǎo)入功能
}
class DataManager {
lazy var importer = DataImporter() //延遲屬性, 用到的時(shí)候才回去加載
var data = String[]()
// 這是提供數(shù)據(jù)管理功能
}
計(jì)算屬性
//結(jié)構(gòu)體
struct Point {
var x = 0.0, y = 0.0
}
//結(jié)構(gòu)體
struct Size {
var width = 0.0, height = 0.0
}
//結(jié)構(gòu)體
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
//getter方法
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
//setter方法, newCenter為setter方法的參數(shù)
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
//聲明一個(gè)結(jié)構(gòu)體
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center //getter方法, 獲取center
square.center = Point(x: 15.0, y: 15.0) //setter方法, 設(shè)置新的center
print("square.origin is now at (\\(square.origin.x), \\(square.origin.y))")
只讀計(jì)算屬性
只有 getter 沒(méi)有 setter 的計(jì)算屬性就是只讀計(jì)算屬性。只讀計(jì)算屬性總是返回一個(gè)值本昏,可以通過(guò)點(diǎn)運(yùn)算符訪問(wèn)供汛,但不能設(shè)置新的值。(類比OC屬性修飾readonly)
注意:必須使用var關(guān)鍵字定義計(jì)算屬性涌穆,包括只讀計(jì)算屬性怔昨,因?yàn)樗麄兊闹挡皇枪潭ǖ摹et關(guān)鍵字只用來(lái)聲明常量屬性宿稀,表示初始化后再也無(wú)法修改的值趁舀。
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double { //只讀計(jì)算屬性的聲明可以去掉get關(guān)鍵字和花括號(hào):
return width * height * depth
}
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \\(fourByFiveByTwo.volume)")
屬性監(jiān)視器
屬性監(jiān)視器監(jiān)控和響應(yīng)屬性值的變化,每次屬性被設(shè)置值的時(shí)候都會(huì)調(diào)用屬性監(jiān)視器祝沸,甚至新的值和現(xiàn)在的值相同的時(shí)候也不例外. (類似于KVO, 但是比KVO方便多了)
可以為屬性添加如下的一個(gè)或全部監(jiān)視器:
willSet在設(shè)置新的值之前調(diào)用
didSet在新的值被設(shè)置之后立即調(diào)用
willSet監(jiān)視器會(huì)將新的屬性值作為固定參數(shù)傳入矮烹,在willSet的實(shí)現(xiàn)代碼中可以為這個(gè)參數(shù)指定一個(gè)名稱,如果不指定則參數(shù)仍然可用罩锐,這時(shí)使用默認(rèn)名稱newValue表示奉狈。
類似地,didSet監(jiān)視器會(huì)將舊的屬性值作為參數(shù)傳入涩惑,可以為該參數(shù)命名或者使用默認(rèn)參數(shù)名oldValue仁期。
注意:willSet和didSet監(jiān)視器在屬性初始化過(guò)程中不會(huì)被調(diào)用,他們只會(huì)當(dāng)屬性的值在初始化之外的地方被設(shè)置時(shí)被調(diào)用境氢。
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) { //willSet監(jiān)視器將表示新值的參數(shù)自定義為newTotalSteps
print("About to set totalSteps to \\(newTotalSteps)")
}
didSet {
if totalSteps > oldValue { //didSet沒(méi)有提供自定義名稱蟀拷,所以默認(rèn)值oldValue表示舊值的參數(shù)名碰纬。
print("Added \\(totalSteps - oldValue) steps")
//注意:如果在didSet監(jiān)視器里為屬性賦值萍聊,這個(gè)值會(huì)替換監(jiān)視器之前設(shè)置的值。
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200 //設(shè)置新值, 調(diào)用willSet
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
//
類型屬性
實(shí)例的屬性屬于一個(gè)特定類型實(shí)例悦析,每次類型實(shí)例化后都擁有自己的一套屬性值寿桨,實(shí)例之間的屬性相互獨(dú)立。
也可以為類型本身定義屬性,不管類型有多少個(gè)實(shí)例亭螟,這些屬性都只有唯一一份挡鞍。這種屬性就是類型屬性。
類型屬性用于定義特定類型所有實(shí)例共享的數(shù)據(jù)预烙,比如所有實(shí)例都能用的一個(gè)常量(就像 C 語(yǔ)言中的靜態(tài)常量)墨微,或者所有實(shí)例都能訪問(wèn)的一個(gè)變量(就像 C 語(yǔ)言中的靜態(tài)變量)。
對(duì)于值類型(指結(jié)構(gòu)體和枚舉)可以定義存儲(chǔ)型和計(jì)算型類型屬性扁掸,對(duì)于類(class)則只能定義計(jì)算型類型屬性翘县。
值類型的存儲(chǔ)型類型屬性可以是變量或常量,計(jì)算型類型屬性跟實(shí)例的計(jì)算屬性一樣定義成變量屬性谴分。
注意:跟實(shí)例的存儲(chǔ)屬性不同锈麸,必須給存儲(chǔ)型類型屬性指定默認(rèn)值,因?yàn)轭愋捅旧頍o(wú)法在初始化過(guò)程中使用構(gòu)造器給類型屬性賦值牺蹄。
//結(jié)構(gòu)體
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 10
// 這里返回一個(gè) Int 值
}
}
//枚舉
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
// 這里返回一個(gè) Int 值
return 20
}
}
//類
class SomeClass {
//這里在xcode8里, class改為static也可以(目前測(cè)試, 結(jié)果都是對(duì)的, 可能還有沒(méi)有考慮到的情況)
class var computedTypeProperty: Int {
// 這里返回一個(gè) Int 值
return 30
}
}
print(SomeStructure.computedTypeProperty) //10
print(SomeStructure.computedTypeProperty) //20
print(SomeClass.computedTypeProperty) //30