Swift 屬性


Swift 屬性將值跟特定的類別、結構或枚舉關聯。

屬性可分為儲存屬性與運算屬性:

#儲存屬性運算屬性
儲存常數或變數作為實例的一部分計算(而不是儲存)一個值
用於類別和結構體用於類別、結構體和枚舉
#

儲存屬性和計算屬性通常用於特定類型的實例。

屬性也可以直接用於型別本身,這種屬性稱為型別屬性。

另外,還可以定義屬性觀察器來監控屬性值的變化,以此來觸發一個自訂的操作。屬性觀察器可以加入到自己寫的儲存屬性上,也可以加入到從父類別繼承的屬性上。


儲存屬性

簡單來說,一個儲存屬性就是儲存在特定類別或結構體的實例裡的常數或變數。

儲存屬性可以是變數儲存屬性(用關鍵字var定義),也可以是常數儲存屬性(用關鍵字let定義)。

  • 可以在定義儲存屬性的時候指定預設值

  • #也可以在建構過程中設定或修改儲存屬性的值,甚至修改常數儲存屬性的值

import Cocoa

struct Number
{
   var digits: Int
   let pi = 3.1415
}

var n = Number(digits: 12345)
n.digits = 67

print("\(n.digits)")
print("\(n.pi)")

以上程式執行輸出結果為:

67
3.1415

考慮以下程式碼:

let pi = 3.1415

程式碼中pi 在定義儲存屬性的時候指定預設值(pi = 3.1415),所以不管你何時實例化結構體,它都不會改變。

如果你定義的是一個常數儲存屬性,如果嘗試修改它就會報錯,如下所示:

import Cocoa

struct Number
{
    var digits: Int
    let numbers = 3.1415
}

var n = Number(digits: 12345)
n.digits = 67

print("\(n.digits)")
print("\(n.numbers)")
n.numbers = 8.7

以上程序,執行會報錯,錯誤如下所示:

error: cannot assign to property: 'numbers' is a 'let' constant
n.numbers = 8.7

意思是'numbers' 是一個常數,你不能修改它。


延遲儲存屬性

延遲儲存屬性是指第一次被呼叫的時候才會計算其初始值的屬性。

在屬性宣告前使用 lazy 來標示一個延遲儲存屬性。

注意:
必須將延遲儲存屬性宣告成變數(使用var關鍵字),因為屬性的值在實例建構完成之前可能無法得到。而常數屬性在構造過程完成之前必須要有初始值,因此無法宣告成延遲屬性。

延遲儲存屬性一般用於:

  • 延遲物件的建立。

  • 當屬性的值依賴其他未知類別

import Cocoa

class sample {
    lazy var no = number() // `var` 关键字是必须的
}

class number {
    var name = "php Swift 教程"
}

var firstsample = sample()
print(firstsample.no.name)

以上程式執行輸出結果為:

php Swift 教程

實例化變數

如果您有過Objective-C 經驗,應該知道Objective-C 為類別實例儲存值和參考提供兩種方法。對於屬性來說,也可以使用實例變數作為屬性值的後端儲存。

Swift 程式語言中把這些理論統一用屬性來實作。 Swift 中的屬性沒有對應的實例變數,屬性的後端儲存也無法直接存取。這就避免了不同場景下存取方式的困擾,同時也將屬性的定義簡化成一個語句。

一個類型中屬性的全部資訊-包括命名、型別和記憶體管理特徵-都在唯一一個地方(型別定義中)定義。


計算屬性

除儲存屬性外,類別、結構體和枚舉可以定義計算屬性,計算屬性不直接儲存值,而是提供一個getter 來取得值,一個可選的setter 來間接設定其他屬性或變數的值。

import Cocoa

class sample {
    var no1 = 0.0, no2 = 0.0
    var length = 300.0, breadth = 150.0
    
    var middle: (Double, Double) {
        get{
            return (length / 2, breadth / 2)
        }
        set(axis){
            no1 = axis.0 - (length / 2)
            no2 = axis.1 - (breadth / 2)
        }
    }
}

var result = sample()
print(result.middle)
result.middle = (0.0, 10.0)

print(result.no1)
print(result.no2)

以上程式執行輸出結果為:

(150.0, 75.0)
-150.0
-65.0

如果計算屬性的 setter 沒有定義表示新值的參數名,則可以使用預設名稱 newValue。


只讀計算屬性

只有 getter 沒有 setter 的計算屬性就是唯讀計算屬性。

只讀計算屬性總是傳回一個值,可以透過點(.)運算子訪問,但不能設定新的值。

import Cocoa

class film {
    var head = ""
    var duration = 0.0
    var metaInfo: [String:String] {
        return [
            "head": self.head,
            "duration":"\(self.duration)"
        ]
    }
}

var movie = film()
movie.head = "Swift 属性"
movie.duration = 3.09

print(movie.metaInfo["head"]!)
print(movie.metaInfo["duration"]!)

以上程式執行輸出結果為:

Swift 属性
3.09

注意:

必須使用var關鍵字定義計算屬性,包含唯讀計算屬性,因為它們的值不是固定的。 let關鍵字只用來宣告常數屬性,表示初始化後再也無法修改的值。


屬性觀察器

屬性觀察器監控和回應屬性值的變化,每次屬性被設定值的時候都會呼叫屬性觀察器,甚至新的值和現在的值相同的時候也不例外。

可以為除了延遲儲存屬性之外的其他儲存屬性新增屬性觀察器,也可以透過重載屬性的方式為繼承的屬性(包括儲存屬性和運算屬性)新增屬性觀察器。

注意:
不需要為無法重載的計算屬性添加屬性觀察器,因為可以透過 setter 直接監控和回應值的變化。

可以為屬性新增如下的一個或全部觀察器:

  • #willSet在設定新的值之前呼叫

  • didSet在新的值設定之後立即呼叫

  • willSet和didSet觀察器在屬性初始化過程中不會被呼叫

import Cocoa

class Samplepgm {
    var counter: Int = 0{
        willSet(newTotal){
            print("计数器: \(newTotal)")
        }
        didSet{
            if counter > oldValue {
                print("新增数 \(counter - oldValue)")
            }
        }
    }
}
let NewCounter = Samplepgm()
NewCounter.counter = 100
NewCounter.counter = 800

以上程式執行輸出結果為:

计数器: 100
新增数 100
计数器: 800
新增数 700

全域變數與局部變數

計算屬性與屬性觀察器所描述的模式也可以用於全域變數和局部變數。

局部變數#全域變數
在函數、方法或閉包內部定義的變數。 函數、方法、閉包或任何類型之外定義的變數。
用於儲存和檢索值。 用於儲存和檢索值。
儲存屬性用於取得和設定值。 儲存屬性用於取得和設定值。
也用來計算屬性。 也用來計算屬性。

類型屬性

類型屬性是寫在類型最外層的花括號({})內。

使用關鍵字 static 定義值類型的類型屬性,關鍵字 class 來為類別定義類型屬性。

struct Structname {
   static var storedTypeProperty = " "
   static var computedTypeProperty: Int {
      // 这里返回一个 Int 值
   }
}

enum Enumname {
   static var storedTypeProperty = " "
   static var computedTypeProperty: Int {
      // 这里返回一个 Int 值
   }
}

class Classname {
   class var computedTypeProperty: Int {
      // 这里返回一个 Int 值
   }
}

注意:
範例中的運算型類型屬性是唯讀的,但也可以定義可讀可寫的運算型類型屬性,跟實例計算屬性的語法類似。


取得和設定型別屬性的值

類似實例的屬性,型別屬性的存取也是透過點運算子(.)來進行。但是,類型屬性是透過類型本身來取得和設置,而不是透過實例。實例如下:

import Cocoa

struct StudMarks {
   static let markCount = 97
   static var totalCount = 0
   var InternalMarks: Int = 0 {
      didSet {
         if InternalMarks > StudMarks.markCount {
            InternalMarks = StudMarks.markCount
         }
         if InternalMarks > StudMarks.totalCount {
            StudMarks.totalCount = InternalMarks
         }
      }
   }
}

var stud1Mark1 = StudMarks()
var stud1Mark2 = StudMarks()

stud1Mark1.InternalMarks = 98
print(stud1Mark1.InternalMarks) 

stud1Mark2.InternalMarks = 87
print(stud1Mark2.InternalMarks)

以上程式執行輸出結果為:

97
87