🔰property wrapper
把真正的資料包裹起來,只開放一個或兩個公開窗口的結構。
Last updated
把真正的資料包裹起來,只開放一個或兩個公開窗口的結構。
Last updated
Swift ⟩ type ⟩ property ⟩ wrapper
A property wrapper is a simple way to apply a common pattern of behavior to a property, it's a way of annotating the property that defines how it will be stored or computed on reading.
built-in property wrappers
如果我們要設計一個「學生」的結構,然後有「分數」這個屬性,但規定不管我們如何輸入這個分數,這個分數都只能介於 0 ~ 100 之間。例如:
如果輸入程式碼的話,要長這樣:
這時要如何設計這個結構 (struct) 呢?首先,嘗試下面的程式碼:
只要這樣設計,加入一個「私密變數」(private var)、再一個「計算屬性」(computed property),就可以達到我們原先的要求:「可以控管學生的分數始終在 0 ~ 100 之間」。
這樣做雖然沒什麼問題,但問題在於:在程式設計中,類似這樣的模式 (pattern) 實在太常出現,例如:「分數在 0 ~ 100 之間」、「評分在 0 ~ 10 之間」、「pH 值在 -1 到 16 之間」、「顏色值在 0 ~ 255 之間」等等,這些一再出現的類似模式,如果每次遇到的時候,都要重新寫一次,那就違反了程式設計的 DRY 綱領:「 Don't Repeat Yourself❗️」,而這也就是為什麼 Swift 要在 5.1 的版本中加入 Property Wrapper 的功能了,Property Wrapper 可以讓我們只設計一次,就解決上面所有的類似模式,不需每次遇到時就重寫一次。
你只要仔細觀察上面「控管學生分數」的例子就會發現:真正的分數是被藏在私密變數 _grade
中,外界無法直接存取這個變數,而是必須透過計算屬性(computed property) grade
來存取它,換句話說,真正的分數等於是被包起來(wrapped)了,而計算屬性 grade
某種程度說來,就是包住它的人(wrapper),而這也就是為什麼 Swift 要將這個新功能稱為:「Property Wrapper」的原因了。
下面我來說明:如何用 Property Wrapper 來達成上面所說的「控管學生的分數」。
首先,我們必須先設計一個可以「控管學生的分數」的結構(struct),設計的方式跟上面的計算屬性 grade
很類似:👉 程式碼:paiza.io
這樣設計的好處是:如果我們有很多科的分數都要登記,例如:國文、英文、數學等,在沒有 property wrapper 之前,我們必須為每一科的分數寫一個計算屬性(computed property),才能確保每個分數都在 0 ~ 100 之間。
但有了 property wrapper 之後就不需要了,因為我們已經將「確保分數都在 0 ~ 100 之間」這樣的機制寫在 @propertyWrapper Grade
之中,所以只要在每科分數的變數前面加上 @Grade
,註明我們要用相同的機制來控管這些分數就可以,如下:
當我們用 @propertyWrapper Grade
(後面開始都簡稱為 @Grade
) 來控管一個分數屬性時,Swift 到底背後做了什麼魔法❓
當我們設計了 @Grade
,雖然可以控管分數,卻也只能控管「0 ~ 100」的分數。如果我們要控管其他範圍的數字,如:pH 值、顏色值等等,又該如何❓
這些問題且待我們有空時,繼續分解。🚧
一個 property wrapper 就是把真正的資料 (圖中的 _value
) 包裹起來,只開放一個或兩個公開的窗口 (圖中的 wrappedValue 與 projectedValue) 來存取這個資料的結構 (通常是 struct,但也可以是 class, enum 等其他型別)。
目前只能存活在別的結構裡面,當作別人的屬性,還不能獨立存在,所以稱為 Property Wrapper。
以上圖為例,我們為 Person
這個結構添加了一個 property wrapper - @Wrapped var name
,但實際上,Swift compiler 會在背後自動產生三個屬性 (如果我們有定義 projectedValue 的話):
name:這是公開屬性,直接與 wrappedValue 對接,用 person.name
就等同於存取 property wrapper 的 wrappedValue。
$name:也是公開屬性,直接與 projectedValue 對接,用 person.$name
等同於存取 property wrapper 的 projectedValue,但此值是有宣告才有,不是每個 property wrapper 都必備。
_name:這是 Person 的私密屬性,代表 property wrapper 本身,只能在 Person 內部使用,在外部並不能用 person._name
來存取。
只能用 var 宣告,不能用 let。
可以在別的 property wrapper 裡面當屬性來用,例如:@UnitInterval
自動編碼 (Codable):👉
#todo
@State 變數的 projectedValue 所傳回的值是 @State 變數本身 (self)。 👉 Property Wrappers in Swift - Swift by Sundell
The @Published property wrapper uses its projectedValue to expose a publisher. 👉 Wrapping your head around Property Wrappers in Swift - Donny Wals
我們輸入的分數
學生實際的分數
說明
-200
0
最低只能 0 分。
60
60
如果是正常的分數,就不改變。
300
100
最高只能 100 分。