Swift Structure 的兩三事
以下做為個人筆記,將 Swift 的 Struct 做一些分解動作。
Structure 沒有繼承 (inheritance)


但是 Structure 有多型 (polymorphism)

Structure 是 call-by-value的 (傳值呼叫,被傳遞的是"複本",非"原件")

(註:Class 是 call-by-reference的)

Structure 是 "不變的"



這裡在 Main 類別裡利用 Swift 的 property observers 語法:didSet {} 來觀察 person 被賦值的情形。
由印出結果看得出來:不論是 person 本身或是其特性 (name) 被賦值時,對 main 來說,其實 person 這個特性的值是一直被取代替換的新值。
結論:Structure 可以被取代,但是它本身就是 "不變的"。
Structure 生來不可變,就算它自己想變也不行!Swift 提供了 mutating 關鍵字讓一切變簡單一些。
下圖展示了 Person 結構自己的 setName() function 嚐試改變 name 特性:

依 Structure 的不變性,若要改變某個 Person 實例的 name 特性,只能回傳一個全新的實例。如下圖

不過在圖九中最後的訊息中,編譯器有提供另一個實作選擇的提示:在 func 前面加上 mutating 這個關鍵字,就可以使 ‘self’ 成為可變 (make ‘self’ mutable)。

將 setName() 這個 function 前綴 ‘mutating’ 後,編譯器就能正常功作了。
此外,這裡將 name 前面前綴了 private ,讓 name 的設定只能透過 setName()。(只是為了強化只能透過 setName() 賦值的語意,這個步驟不是必要的)
下方的 name 賦值方式因應改變,但是注意:每次 name 的變更還是一樣會造成 main 裡面的 person 不斷地被重新賦值,這個 "不變的" 特性就是不會變。
這裡進行另一個討論:

這裡要討論的是:第 29、30 行的輸出為何?
關鍵在於第 26、27 兩行設定 name 的方式,是等價的嗎?
答案是:不同。
第29行會印出「Tony」;
第30行會印出「Tony Stark」。
原因是在第26行執行後,其 ironMan 和第25行的 ironMan 已經是不同一個了。
(此例中,加上了 readName() 讓 name 可以被類別外程式讀取)
SwiftUI 有額外的語法支援: @State 屬性
在 SwiftUI 中,Structure 隨處可見,所以我們無法不夠了解 Structure 這個 "不變性"。從下面這個例子來討論。假設有一個簡單的 UI 如下:

這個例子中,整個畫面由一個 Text 元件和一個 Button 元件所構成。
按下「換人彈指!」這個按鈕後,顯示的文字會從「I am inevitable!」變成「I am IronMan!」。
也就是說,在 Button 被點擊後,Text 顯示的文字要被改變。
對應的程式碼如下:

上圖中,Xcode 提示出了一個錯誤訊息,指出 message 這個特性因為是被宣告在 ContentView 這個 Structure 裡,所以若要改變它,也會遇到 "不變性" 所產生的提示。
依前文所言,若希望 message 可變,可以提供一個前綴 mutating 的設值 function 來改變 message;但是,這裡的 self.message 的值同時也是 body 這個 property 的一部分,而 body 也是不可變的。會產生:Cannot use mutating member on immutable value: ‘self’ is immutable 的錯誤。(這裡的 immutable value 指的是 body)
此外,就算能改變 body 裡面的 message 值,要怎麼通知畫面重繪 (因為 body 改變了)呢?
結論是:這裡不是使用 Swift 提供的 mutating 機制,而是使用 SwiftUI 提供的 @State 這個 attribute。

