Delegation實際範例
實際使用時的語法結構
白鬍子教授在Developing iOS 8 Apps with Swift (Stanford)的第6課Delegation & Gestures中,示範了delegation(他用的是data source,與delagate意思相同但主要用於資料部份)的用法與使用時機,這是真正用Delegation的語法結構:
View.swift檔案
protocol FaceViewDataSource: class {
func smilinessForFaceView(sender: FaceView)-> Double?
}
class FaceView: UIView {
weak var dataSource: FaceViewDataSource?
//...
let smiliness = dataSource?.smilinessForFaceView(self) ?? 0.0
}
Controller.swift檔案
class HappinessViewController: UIViewController, FaceViewDataSource{
@IBOutlet weak var faceView: FaceView!{
didSet {
faceView.dataSource = self
}
}
//...
func smilinessForFaceView(sender: FaceView)-> Double? {
return Double(happiness-50)/50
}
}
這幾段程式碼寫來輕鬆,實際上是很有深度的語法。以下分幾個部份來說說。
白鬍子教授應該稱為Paul Hegarty教授,是一位長期教iOS開發的Stanford軟體工程教授,曾經參與NextSTEP開發(Mac OSX與iOS的前身),在網路上關於他的背景資料並不多。
弱(weak)參考
protocol
的後面有個:class
,強制這個protocol
只能讓class
來遵守(conform)它,主要是為了配合下面這段的weak
關鍵字。這是為了要使用弱(weak)參考,以避免Retain cycles(或strong reference cycles)。只有class
才能使用弱(weak)參考,structs和enums都是value type,只有強(strong)參考。Retain cycles可以參考最後面的參考資料連結。
這一段的語法在Swift的Beta版本到目前已經有變動,網路上有些查到的資料是舊版的,白鬍子的語法是現行版本可用的。
weak var dataSource: FaceViewDataSource?
在Apple的SnakesAndLadders範例中,一樣有使用了Delegate,但卻不是用這種嚴格的寫法,那又是為何?難道不會有Retain cycles(或strong reference cycle)嗎?答案是沒有,那段範例沒有這個問題,那段只是單純的兩個物件實例的互動關係而已。
但是在MVC的實際語法中,我們常會用var delegate = self
或用self
傳入protocol
方法中的sender
值,此時就存在Retain cycles(或strong reference cycle)的"風險" - 「彼此互相保持住參考造成循環」,結果就是打結了。雖然Swift語言中並不需要作記憶體管理,也不會讓你作。這樣的寫法是為了避免"風險",所以變成一種通則的語法樣版。像下面這樣:
protocol ProtocolNameDelegate: class {
// Protocol stuff goes here
}
class SomeClass {
weak var delegate: ProtocolNameDelegate?
}
Optional值
delegate是weak
之外,還是個optional
(弱(weak)參考必定為optional
類型。),這是代表它"可能"不存在,有可能有,也有可能沒有。白鬍子教授的例子是在protocol
裡的方法,連回傳值也是optional
的,這連帶會使用到optional chain
的語法,以及像??
(Nil Coalescing Operator)為了處理optional
值敘述,呼應所謂的View與Contoller間的"盲溝通(blind communication)"的說法,因為"瞎了",有沒有其實也要"摸一摸"才知道:
let smiliness = dataSource?.smilinessForFaceView(self) ?? 0.0
雖然它這個例子中的sender
還沒有用途,不過這個寫法是要保留一個彈性,可以由sender
知道是由哪個實例正在建立委託的。
實作順序
protocol
與建立委託參考的View寫在同一個檔案裡,這個實作(撰寫)的順序是:
- 在View檔案(或建立委託參考的類)建立一個藍圖或原型。protocol的宣告也在這個檔案裡面。
- 在Controller檔案裡,Controller遵守(conform)這個protocol。在Controller裡實作所需的方法。
- hook的時機:在Controller建立一個連接View的Outlet(出口),用didSet的property observer方式,指定View實例的delegate(data source)為
self
(Conctroller:「就是偶啦!」)
第1點是最常讓人搞混的,protocol
寫在這個檔案(View)裡,卻並不是在這個檔案中實作。你可以把它當作是一個類型(Type)的宣告比較合理,它是讓這個檔案裡的View,也就是要建立委託參考的類,能用於建立一個這種類型的參考,並呼叫protocol
裡的方法,傳入self
到方法的sender
參數中。
參考資料
Developing iOS 8 Apps with Swift (Stanford)
Use Weak References to Avoid Retain Cycles
How can I make a weak protocol reference in 'pure' Swift (w/o @objc)