標籤:ios swift 程式設計語言
屬性將值跟特定的類、結構或枚舉關聯。儲存屬性儲存區常量或變數作為執行個體的一部分,計算屬性計算(而不是儲存)一個值。計算屬性可以用於類、結構體和枚舉裡,儲存屬性只能用於類和結構體。
儲存屬性和計算屬性通常用於特定類型的執行個體,但是,屬性也可以直接用於類型本身,這種屬性稱為類型屬性。
另外,還可以定義屬性監視器來監控屬性值的變化,以此來觸發一個自訂的操作。屬性監視器可以添加到自己寫的儲存屬性上,也可以添加到從父類繼承的屬性上。
儲存屬性
簡單來說,一個儲存屬性就是儲存在特定類或結構體的執行個體裡的一個常量或變數,儲存屬性可以是變數儲存屬性(用關鍵字var定義),也可以是常量儲存屬性(用關鍵字let定義)。
可以在定義儲存屬性的時候指定預設值,請參考構造過程一章的預設屬性值一節。也可以在構造過程中設定或修改儲存屬性的值,甚至修改常量儲存屬性的值,請參考構造過程一章的在初始化階段修改常量儲存屬性一節。
下面的例子定義了一個名為FixedLengthRange的結構體,他描述了一個在建立後無法修改值域寬度的區間:
struct FixedLengthRange { var firstValue: Int let length: Int}var rangeOfThreeItems =FixedLengthRange(firstValue: 0, length: 3)// 該區間表示整數0,1,2rangeOfThreeItems.firstValue = 6// 該區間現在表示整數6,7,8
FixedLengthRange的執行個體包含一個名為firstValue的變數儲存屬性和一個名為length的常量儲存屬性。在上面的例子中,length在建立執行個體的時候被賦值,因為它是一個常量儲存屬性,所以之後無法修改它的值。
常量和儲存屬性
如果建立了一個結構體的執行個體並賦值給一個常量,則無法修改執行個體的任何屬性,即使定義了變數儲存屬性:
let rangeOfFourItems =FixedLengthRange(firstValue: 0, length: 4)// 該區間表示整數0,1,2,3rangeOfFourItems.firstValue = 6// 儘管firstValue詩歌變數屬性,這裡還是會報錯
因為rangeOfFourItems聲明成了常量(用let關鍵字),即使firstValue是一個變數屬性,也無法再修改它了。
這種行為是由於結構體(struct)屬於值類型。當值類型的執行個體被聲明為常量的時候,它的所有屬性也就成了常量。
屬於參考型別的類(class)則不一樣,把一個參考型別的執行個體賦給一個常量後,仍然可以修改執行個體的變數屬性。
延遲儲存屬性
延遲儲存屬性是指當第一次被調用的時候才會計算其初始值的屬性。在屬性聲明前使用@lazy來標示一個延遲儲存屬性。
注意:
必須將延遲儲存屬性聲明成變數(使用var關鍵字),因為屬性的值在執行個體構造完成之前可能無法得到。而常量屬性在構造過程完成之前必須要有初始值,因此無法聲明成延遲屬性。
延遲屬性很有用,當屬性的值依賴於在執行個體的構造過程結束前無法知道具體值的外部因素時,或者當屬性的值需要複雜或大量計算時,可以只在需要的時候來計算它。
下面的例子使用了延遲儲存屬性來避免複雜類的不必要的初始化。例子中定義了DataImporter和DataManager兩個類,下面是部分代碼:
class DataImporter { /* DataImporter 是一個將外部檔案中的資料匯入的類。 這個類的初始化會消耗不少時間。 */ var fileName = "data.txt" // 這是提供資料匯入功能} class DataManager { @lazy var importer = DataImporter() var data = String[]() // 這是提供資料管理功能} let manager = DataManager()manager.data += "Some data"manager.data += "Some more data"// DataImporter 執行個體的 importer 屬性還沒有被建立
DataManager類包含一個名為data的儲存屬性,初始值是一個空的字串(String)數組。雖然沒有寫出全部代碼,DataManager類的目的是管理和提供對這個字串數組的訪問。
DataManager的一個功能是從檔案匯入資料,該功能由DataImporter類提供,DataImporter需要消耗不少時間完成初始化:因為它的執行個體在初始化時可能要開啟檔案,還要讀取檔案內容到記憶體。
DataManager也可能不從檔案中匯入資料。所以當DataManager的執行個體被建立時,沒必要建立一個DataImporter的執行個體,更明智的是當用到DataImporter的時候才去建立它。
由於使用了@lazy,importer屬性只有在第一次被訪問的時候才被建立。比如訪問它的屬性fileName時:
println(manager.importer.fileName)// DataImporter 執行個體的 importer 屬性現在被建立了// 輸出 "data.txt”
儲存屬性和執行個體變數
如果您有過 Objective-C 經驗,應該知道有兩種方式在類執行個體儲存值和引用。對於屬性來說,也可以使用執行個體變數作為屬性值的後端儲存。
Swift 程式設計語言中把這些理論統一用屬性來實現。Swift中的屬性沒有對應的執行個體變數,屬性的後端儲存也無法直接存取。這就避免了不同情境下訪問方式的困擾,同時也將屬性的定義簡化成一個語句。一個類型中屬性的全部資訊——包括命名、類型和記憶體管理特徵——都在唯一一個地方(類型定義中)定義。
計算屬性
除儲存屬性外,類、結構體和枚舉可以定義計算屬性,計算屬性不直接儲存值,而是提供一個 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(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.centersquare.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)”
這個例子定義了 3 個幾何形狀的結構體:
Point封裝了一個(x, y)的座標
Size封裝了一個width和height
Rect表示一個有原點和尺寸的矩形
Rect也提供了一個名為center的計算屬性。一個矩形的中心點可以從原點和尺寸來算出,所以不需要將它以顯式聲明的Point來儲存。Rect的計算屬性center提供了自訂的 getter 和 setter 來擷取和設定矩形的中心點,就像它有一個儲存屬性一樣。
例子中接下來建立了一個名為square的Rect執行個體,初始值原點是(0, 0),寬度高度都是10。藍色正方形。
square的center屬性可以通過點運算子(square.center)來訪問,這會調用getter 來擷取屬性的值。跟直接返回已經存在的值不同,getter 實際上通過計算然後返回一個新的Point來表示square的中心點。如代碼所示,它正確返回了中心點(5, 5)。
center屬性之後被設定了一個新的值(15, 15),表示向右上方移動正方形到橙色正方形的位置。設定屬性center的值會調用 setter 來修改屬性origin的x和y的值,從而實現移動正方形到新的位置。
便捷 setter 聲明
如果計算屬性的 setter 沒有定義表示新值的參數名,則可以使用預設名稱newValue。下面是使用了便捷 setter 聲明的Rect結構體代碼:
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) } }}
唯讀計算屬性
只有 getter 沒有 setter 的計算屬性就是唯讀計算屬性。唯讀計算屬性總是返回一個值,可以通過點運算子訪問,但不能設定新的值。
注意:
必須使用var關鍵字定義計算屬性,包括唯讀計算屬性,因為他們的值不是固定的。let關鍵字只用來聲明常量屬性,表示初始化後再也無法修改的值。
唯讀計算屬性的聲明可以去掉get關鍵字和花括弧:
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)println("the volume of fourByFiveByTwois \(fourByFiveByTwo.volume)")// 輸出 "the volumeof fourByFiveByTwo is 40.0"
這個例子定義了一個名為Cuboid的結構體,表示三維空間的立方體,包含width、height和depth屬性,還有一個名為volume的唯讀計算屬性用來返回立方體的體積。設定volume的值毫無意義,因為通過width、height和depth就能算出volume。然而,Cuboid提供一個唯讀計算屬性來讓外部使用者直接擷取體積是很有用的。
Swift 的儲存屬性和計算屬性