讀古今文學網 > iOS編程基礎:Swift、Xcode和Cocoa入門指南 > 3.5 setter觀察者 >

3.5 setter觀察者

計算變量並不需要成為存儲變量門面,這一點與你想的可能會不同。這是因為Swift提供了另一個漂亮的特性,可以讓你將功能注入存儲變量的setter中,即setter觀察者。這些函數會在其他代碼設置存儲變量前後被調用。

聲明具有setter觀察者變量的語法非常類似於聲明計算變量的語法;你可以編寫一個willSet函數、一個didSet函數,二者也可以都提供:


var s = "whatever" { 1
    willSet { 2
        print(newValue) 3
    }
    didSet { 4
        print(oldValue) 5
        // self.s = "something else"
    }
}  

1變量必須是var(不能是let)。可以為它賦初值,後跟一對花括號。

2willSet函數,如果有,那就是willSet,後跟一對花括號,裡面是函數體。當其他代碼設置該變量時它會被調用,就在變量接收到新值之前。

3在默認情況下,willSet函數會將接收到的新值設為newValue。你可以在單詞willSet後面的圓括號中提供不同的名字來改變這一點。舊值依然位於存儲變量中,willSet函數可以訪問到它。

4didSet函數,如果有,那就是didSet,後跟一對花括號,裡面是函數體。當其他代碼設置該變量時它會被調用,就在變量接收到新值之後。

5在默認情況下,didSet函數會接收到舊值,它已經被變量值所替換,名字為oldValue。你可以在單詞didSet後面的圓括號中提供不同的名字來改變這一點。新值已經位於存儲變量中,didSet函數可以訪問到它。此外,didSet函數也可以將存儲變量設為不同的值。

如果存儲變量被初始化了或是didSet函數修改了存儲變量值,那麼Setter觀察者函數就不會被調用,這是個循環!

實際上,在使用Objective-C中的Setter重寫的大多數情況下,相對於計算變量來說,我更傾向於使用Setter觀察者。如下示例來自於Apple提供的代碼(Master–Detail Application模板),它說明了一種典型場景,即在設置了某個屬性後改變界面:


var detailItem: AnyObject? {
    didSet {
        // Update the view.
        self.configureView
    }
}  

這是視圖控制器類的一個實例屬性。每次修改該屬性時,我們都需要改變界面,因為界面的一部分職責是顯示該屬性的值。因此,每次設置屬性時,我們只需調用一個實例方法即可。該實例方法會讀取屬性值並相應地設置界面。

如下示例來自於我所編寫的代碼,我們不僅要修改界面,還要將設置的值限定在一個範圍內:


var angle : CGFloat = 0 {
    didSet {
        // angle must not be smaller than 0 or larger than 5
        if self.angle < 0 {
            self.angle = 0
        }
        if self.angle > 5 {
            self.angle = 5
        }
        // modify interface to match
        self.transform = CGAffineTransformMakeRotation(self.angle)
    }
}  

計算變量是沒有Setter觀察者的,它也不需要!有一個Setter函數,在設置值時的一些額外處理可以直接在該Setter函數中以編程的方式完成。