6.%d Swift Study — Computed Property

春麗 S.T.E.M.
9 min readJul 27, 2021

--

目錄

⦿ 計算屬性
⦿ 書中的例子
⦿ getter、setter
⦿ Xcode
⦿ 最後設置

計算屬性

首先,看到下面的例子:

class GameCharacter {     var hpValue: Double = 100
var defenceValue: Double = 300
var totalDefence: Double {
get {
return (defenceValue + 0.1 * hpValue)
}
set(levelUp) {
hpValue = hpValue * (1 + levelUp)
defenceValue = defenceValue * (1 + levelUp)
}
}
}
let oneChar = GameCharacter()
print(oneChar.totalDefence)
oneChar.totalDefence = 0.05
print("血量:\(oneChar.hpValue), 防禦力:\(oneChar.defenceValue)")

一個類別(class)建立後,宣告一個實體 oneChar,此時將屬性 print 出來,會得到 300 + 0.1 * 100 = 310;倘若今天將屬性設為 0.05,屬性會依據這個設置做計算,hpValue = 100 * 1.05 = 105;defenceValue = 300 * 1.05 = 315,這時,我們稱 totalDefence 為計算屬性

get{ } 用來存取(getter),set{ } 用來設置(setter)。

繼續閱讀|回目錄

書中的例子

再看到例子,如下:

class Square {
var width: Double = 0
var area: Double {
get {
return width * width
}
set(newArea) {
width = sqrt(newArea)
}
}
}
let bigSquare = Square()
bigSquare.width = 10
print(bigSquare.area)

同樣生成實體,將 width 設置為 10,此時存取 area 為 100.0,同樣是透過 getter 與 setter 對屬性做了計算。

getter 的 { } 裡的 return 是回傳計算結果,setter 的 { } 裡也是計算。

在這個例子裡,若 bigSquare.area = 81,print(bigSquare.width) 會得到 9.0(Double),因此,我們可以知道兩個屬性,widtharea 是環環相扣,並互相影響的,就因為 area 是計算屬性(Computed Property)。

繼續閱讀|回目錄

getter、setter

如果邊長 x 邊長等於面積,邊長就等於面積開平方,那麼,getter 與 setter 豈不是在說明同件事嗎?是的。

但 getter、setter 是互相影響的兩個函數嗎?不是,或者說問題不能這樣問。

當 area 被設定時會進到 setter,也就是 newArea 被設定了,那麼,width 也跟著被設定了;倘若 bigSquare 建立時未設定屬性,這時會進到 getter,存取這個 computed property 會得到 0,因為初始值為 0,這即是說 getter 與 setter 是分開作用的。

第一段裡,兩個儲存屬性(hpValue、defenceValue)、一個計算屬性(totalDefence),兩個儲存屬性都對計算屬性造成了影響,或者說計算屬性靠他們初始化

從前面兩段的例子,在 set( ){ } 的參數,一個是 levelUp、一個是 newArea,( ) 都可以簡化不寫,以 Swift 的內部設定值 `newValue` 來改寫。

      set {
hpValue = hpValue * (1 + newVlue)
defenceValue = defenceValue * (1 + newValue)
}
set {
width = sqrt(newValue)
}

在 Swift 裡,我們學到許多這個語言的規則,例如計算屬性`newValue`,enum 的 rawValue 屬性,如此,enum 便可藉由 dot syntax 存取 case 的設定值,當 rawValue字串型別時, .rawValue 還可直接得到與 case 相同的 String

計算屬性中,getter 一定要有(像廢話,因為屬性通常得存取),setter 則不一定,雖然 getter 與 setter 同樣都是計算,只是一個沒有 newValue,一個有。

繼續閱讀|回目錄

Xcode

計算屬性可以實作在什麼地方呢?

在 View Controller 下,若這樣設置:

var input: Double {
get {
return Double(resultLabel.text!)!
}
set {
resultLabel.text = "\(decimalPoint(rem: newValue))"
stillTyping = false
}
}

可以看到這個計算屬性,getter 裡面是用 Double 去轉換 label 的字串,也就是我們存取 input 時,會得到 String 型別轉成 Double 型別的數字。

在 setter 裡,label 的 text 是以 newValue 代入一個叫做 decimalPoint 的函式來獲取,這個函式是把 Double or Int 型別轉成 String,且如果這個 input 被設定時,stillTyping 就是 false

試想,這樣的設置可以用來做什麼事呢?

於是像這樣拉了一個 IBOutlet,兩個 IBAction,而 button 1、9 使用同一個 IBAction,我們可以透過 sender.currentTitle! 去存取你按的是 1 抑或是 9,而這跟在 Outlet Collection 中設置每個 button 為不同 tag 有些類似,tag 是為了區分按下不同 button 時要做不同的事,而 sender.currentTitle! 則是直接取用 button 設置的 title,當然,此時的 title 就是 String 型別的數字

當這兩顆 button 按下去,會直接給 resultLabel 賦值。

注意!並不是給 input 賦值,因為 \(input) 相當於存取 input,所以會使用 input 的 getter,而此時 input 是由 resultLabel.text 而來,只是轉成 Double 型別。

所以到這邊,我們幾乎可以確定 1 和 9 就是幫 resultLabel 轉成 Double。

接著,當按下 = 這個 button,會直接給 input 賦值,參照如下:

我們可以看到,當按下 button 1,原先 label 顯示的 8 變成 8.0,這是因為 input 的型別為 Double,Double 回頭影響了 label。

而按下 =,label 又變成了 666.0,是因為 input 變為 666,又回頭影響了 label。(真的是這樣嗎?)

那麼,再度按下 button 19 呢?再怎麼按都是 666.0,當然,回頭按 =,也還是一樣 666.0

繼續閱讀|回目錄

最後設置

接著,稍微改變一下設置:

將前面說的 .currentTitle 拿來用,此時要記得,若當初設置 IBAction 時沒有選擇 UIButton,即 sender 是 Any,是沒有辦法用 dot syntax 去存取此屬性的。

最後結果如下:

在按完 1 後又想按 9,卻馬上回報錯誤,可以看到 8.0 是存取了 input 將 label.text 轉換成 Double,而 1 是 currentTitle,但按下 9(或再按 1) ,此時 input 沒辦法將 8.0 + 1 轉成 Double,自然會回報錯誤。

若要正常操作,需要將 currentTitle 也轉換為 Double,將加總結果再賦值給 label,如下:

我們再看看是否正常,如下:

於是可看到按 1 加 1,按 9 加 9,而按 = 就是又回到 666.0,計算屬性是不是有點意思呢?

在這其中要特別注意的是 sender.currentTitle,必須要先經過 !force unwrap,在用 Double 將 String 轉型也還需要一次 !

由於 Swift 的安全檢查,避免在做轉型的時候出錯,當你 force unwrap 時,Xcode 也會提供解決辦法。如下:

第二個解決辦法便是確定有值,所以取值,實際上程式寫成不能轉型成double便會回報錯誤。

第一個解決辦法是無值時候設置初值,如下:

如果你確定 sender.currentTitle 確定有值就放心地 unwrap,而 Double 將 String 轉型若不是那麼確定,就給定一個初值為 0,經過修改後,APP 便比較安全(不容易當)了。

這次就分享到這,感謝您的閱讀。

繼續閱讀|回目錄

附上 Reference:

--

--

春麗 S.T.E.M.
春麗 S.T.E.M.

Written by 春麗 S.T.E.M.

Do not go gentle into that good night, Old age should burn and rave at close of day; Rage, rage, against the dying of the light.

No responses yet