屬性
- 存儲(chǔ)屬性
- 計(jì)算屬性
- 屬性觀察器
- 全局變量和局部變量
- 類型屬性
存儲(chǔ)屬性
struct FixedLengthRange {
var firstValue: Int
let length: Int
}
FixedLengthRange的實(shí)例包含一個(gè)名為firstValue的變量存儲(chǔ)屬性和一個(gè)名為length的常量存儲(chǔ)屬性奕谭。
常量結(jié)構(gòu)體的存儲(chǔ)屬性
如果創(chuàng)建了一個(gè)結(jié)構(gòu)體的實(shí)例并將其賦值給一個(gè)常量血柳,則無法修改該實(shí)例的任何屬性蹬昌,即使定義了變量存儲(chǔ)屬性:
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
rangeOfFourItems.firstValue = 6
// 盡管 firstValue 是個(gè)變量屬性皂贩,這里還是會(huì)報(bào)錯(cuò)
這種行為是由于結(jié)構(gòu)體(struct)屬于值類型明刷。當(dāng)值類型的實(shí)例被聲明為常量的時(shí)候满粗,它的所有屬性也就成了常量映皆。
屬于引用類型的類(class)則不一樣。把一個(gè)引用類型的實(shí)例賦給一個(gè)常量后组去,仍然可以修改該實(shí)例的變量屬性步淹。
延遲存儲(chǔ)屬性
延遲存儲(chǔ)屬性是指當(dāng)?shù)谝淮伪徽{(diào)用的時(shí)候才會(huì)計(jì)算其初始值的屬性缭裆。在屬性聲明前使用lazy來標(biāo)示一個(gè)延遲存儲(chǔ)屬性澈驼。
注意:
必須將延遲存儲(chǔ)屬性聲明成變量(使用var關(guān)鍵字),因?yàn)閷傩缘某跏贾悼赡茉趯?shí)例構(gòu)造完成之后才會(huì)得到挎塌。而常量屬性在構(gòu)造過程完成之前必須要有初始值勃蜘,因此無法聲明成延遲屬性缭贡。
下面的例子使用了延遲存儲(chǔ)屬性來避免復(fù)雜類中不必要的初始化。例子中定義了DataImporter和DataManager兩個(gè)類谍失,下面是部分代碼:
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()
var data = [String]()
// 這是提供數(shù)據(jù)管理功能
}
let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// DataImporter 實(shí)例的 importer 屬性還沒有被創(chuàng)建
print(manager.importer.fileName)
// DataImporter 實(shí)例的 importer 屬性現(xiàn)在被創(chuàng)建了
// 輸出 "data.txt”
注意:
如果一個(gè)被標(biāo)記為lazy的屬性在沒有初始化時(shí)就同時(shí)被多個(gè)線程訪問止潮,則無法保證該屬性只會(huì)被初始化一次喇闸。
計(jì)算屬性
計(jì)算屬性不直接存儲(chǔ)值,而是提供一個(gè) getter 和一個(gè)可選的 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)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// 輸出 "square.origin is now at (10.0, 10.0)”
便捷 setter 聲明
如果計(jì)算屬性的 setter 沒有定義表示新值的參數(shù)名痢艺,則可以使用默認(rèn)名稱newValue堤舒。下面是使用了便捷 setter 聲明的Rect結(jié)構(gòu)體代碼:
struct AlternativeRect {
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)
}
}
}
只讀計(jì)算屬性
只有 getter 沒有 setter 的計(jì)算屬性就是只讀計(jì)算屬性舌缤。只讀計(jì)算屬性總是返回一個(gè)值国撵,可以通過點(diǎn)運(yùn)算符訪問介牙,但不能設(shè)置新的值环础。
注意:
必須使用var關(guān)鍵字定義計(jì)算屬性,包括只讀計(jì)算屬性线得,因?yàn)樗鼈兊闹挡皇枪潭ǖ墓峁场et關(guān)鍵字只用來聲明常量屬性角雷,表示初始化后再也無法修改的值。
只讀計(jì)算屬性的聲明可以去掉get關(guān)鍵字和花括號(hào):
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
return width * height * depth
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// 輸出 "the volume of fourByFiveByTwo is 40.0
屬性觀察器
可以為屬性添加如下的一個(gè)或全部觀察器:
可以為除了延遲存儲(chǔ)屬性之外的其他存儲(chǔ)屬性添加屬性觀察器季二,也可以通過重寫屬性的方式為繼承的屬性(包括存儲(chǔ)屬性和計(jì)算屬性)添加屬性觀察器。
- willSet在新的值被設(shè)置之前調(diào)用
- didSet在新的值被設(shè)置之后立即調(diào)用
willSet觀察器會(huì)將新的屬性值作為常量參數(shù)傳入刻蚯,在willSet的實(shí)現(xiàn)代碼中可以為這個(gè)參數(shù)指定一個(gè)名稱,如果不指定則參數(shù)仍然可用桑嘶,這時(shí)使用默認(rèn)名稱newValue表示炊汹。
類似地,didSet觀察器會(huì)將舊的屬性值作為參數(shù)傳入逃顶,可以為該參數(shù)命名或者使用默認(rèn)參數(shù)名oldValue讨便。
注意:
父類的屬性在子類的構(gòu)造器中被賦值時(shí),它在父類中的willSet和didSet觀察器會(huì)被調(diào)用以政。
例子如下:
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
注意:
如果在一個(gè)屬性的didSet觀察器里為它賦值霸褒,這個(gè)值會(huì)替換該觀察器之前設(shè)置的值。
全局變量和局部變量
類型屬性
即static關(guān)鍵字標(biāo)注的屬性盈蛮,只有一份废菱,可用在結(jié)構(gòu)體,枚舉和類中抖誉。