讀古今文學網 > iOS編程基礎:Swift、Xcode和Cocoa入門指南 > 3.4 計算變量 >

3.4 計算變量

到目前為止,本章所介紹的變量都是存儲下來的變量,就像盒子一樣。變量是個名字,就像盒子一樣;值可以通過賦給變量放到盒子中,然後等待通過引用該變量進行獲取,只要變量存在就行。

此外,變量還可以計算出來。這意味著變量不再持有值,而是持有函數。在給變量賦值時,函數setter會被調用。當引用變量時,另一個函數getter會被調用。如下代碼演示了聲明計算變量的語法:


var now : String { 1
    get { 2
        return NSDate.description 3
    }
    set { 4
        print(newValue) 5
    }
}  

1變量必須是個var(不能是let)。其類型必須要顯式聲明,後跟一對花括號。

2getter函數叫作get。注意到這裡並沒有正式的函數聲明;單詞get後跟一對花括號,裡面是函數體。

3getter函數必須要返回與變量類型相同的值。

4setter函數叫作set。這裡並沒有正式的函數聲明;單詞set後跟一對花括號,裡面是函數體。

5setter的行為就像是接收一個參數的函數。在默認情況下,參數通過局部名newValue進入setter函數中。

如下代碼演示了計算變量的用法,這與其他變量沒什麼大的差別。該賦值時就賦值,該使用時就使用。不過在幕後,setter與getter函數會被調用:


now = \"Howdy\" // Howdy 1
print(now) // 2015-06-26 17:03:30 +0000 2  

1為now賦值會調用其setter。傳遞給調用的參數就是所賦的值,這裡就是\"Howdy\"。該值進入set函數後成為newValue。set函數會將newValue打印到控制台上。

2獲取now會調用其getter。get函數會獲取到當前的日期時間,然後將其轉換為字符串並返回。接下來將該字符串打印到控制台上。

注意到第1行將now設為\"Howdy\"時,字符串\"Howdy\"並沒有存儲下來。比如,它對於第2行的now值就沒起任何作用。set函數可以存儲值,不過它無法將其存儲到計算變量中;計算變量是不可存儲的!它只是調用getter與setter函數的便捷方法而已。

上述語法有幾個變種:

·set函數的參數名不一定非得叫作newValue。要想指定不同的名字,將其放到單詞set後面的圓括號中即可,如下代碼所示:


set (val) { // now you can use \"val\" inside the setter function body  

·不一定要有setter。如果省略setter,那麼變量就變成只讀的了。為其賦值會導致編譯器報錯。沒有setter的計算變量是Swift中創建只讀變量的主要方式。

·一定要有getter!如果沒有setter,那麼單詞get與後面的花括號就可以省略了。如下代碼是聲明只讀變量的合法方式:


var now : String {
    return NSDate.description
}  

計算變量在很多地方都很有用。下面是其在實際使用中經常用到的地方:

只讀變量

計算變量是創建只讀變量最簡單的方式,只需在聲明中省略setter即可。通常情況下,變量是全局變量或屬性;局部只讀變量的意義並不大。

函數門面

如果每次需要一個值時,它都會由一個函數計算出來,那麼可以通過更簡單的語法將其表示為一個只讀的計算變量。如下示例來自我所編寫的代碼:


var mp : MPMusicPlayerController {
    return MPMusicPlayerController.systemMusicPlayer
}  

每次想要引用時,都可以調用MPMusicPlayerController.systemMusicPlayer(),通過簡單的名字mp來引用會更加緊湊一些。mp表示一個事物而非動作表現,因此將mp當作變量會更好一些,這樣在所有出現mp的地方,它都表示一個事物而非返回事物的函數。

其他變量的門面

計算變量可以位於存儲變量之前,作為一個守護者,來確定如何設置和獲取那些存儲變量。這是相比於Objective-C的訪問器方法的。在極端情況下,公開的計算變量是由一個私有的存儲變量所維護的:


private var _p : String = \"\"
var p : String {
    get {
        return self._p
    }
    set {
        self._p = newValue
    }
}  

這個示例本身沒什麼意義,因為對於訪問器沒什麼可做的:我們只是直接設置和獲取私有的存儲變量而已,因此p與_p之間並沒有什麼實際的差別。但是基於該模板,你可以添加一些功能,在設置和獲取時完成一些額外的事情。

正如上面的示例所示,計算實例屬性函數可以引用其他實例屬性,還可以調用實例方法。這是很重要的,因為一般來說,存儲屬性的初始化器這兩件事都做不了。計算屬性之所以可以,是因為直到實例存在了才可以調用它的函數。

如下示例演示了如何將計算變量用作存儲門面。類有一個實例屬性,它存儲的數據很大,並且可以為nil(它是一個Optional,稍後將會介紹):


var myBigDataReal : NSData! = nil  

當應用進入後台時,我想減少內存使用(因為iOS會殺掉佔據大量內存的後台應用)。因此,我計劃將myBigDataReal數據保存為文件並存儲到磁盤上,然後將變量本身設為nil,這樣可以釋放內存中的數據。現在來考慮當應用回到前台,並且代碼嘗試獲取myBigDataReal時會發生什麼。如果它不為nil,那麼我們只需獲取其值即可。但如果它為nil,可能是因為我們將其值保存到了磁盤上。現在我想讀取磁盤來恢復其值,然後獲取。這正是計算變量門面的用武之地:


var myBigData : NSData! {
    set (newdata) {
        self.myBigDataReal = newdata
    }
    get {
        if myBigDataReal == nil {
            // ... get a reference to file on disk, f ...
            self.myBigDataReal = NSData(contentsOfFile: f)
            // ... erase the file ...
        }
        return self.myBigDataReal
    }
}