天天看點

利用 Delegate Design Pattern 進行 View Controller 之間的溝通

如果你是 Objective-C / Cocoa Touch 的重度使用者,那麼你一定被一個東西困擾過:不同的 View Controller 之間,如果互相進行溝通?

舉個常見的例子。假設我們手頭上有一個 UIViewController 執行個體,名叫 parentViewController ,在這個 parentViewController 生命周期的某個節點,它需要以 modal view controller 的形式 present 一個 UIViewController 執行個體。這個将要被 present 的 UIViewController 執行個體名叫 childViewController。

也就是說,在 parentViewController 的代碼中,我們應該會看到以下這句話。

以上的描述和代碼都非常的順其自然。然而我們很快會發現一個問題,當這個 childViewController 需要被 dismiss 時,由哪個 view controller 執行個體負責 dismiss ?

此時答案不唯一。我們有兩種選擇,兩種都可行,但有差別。

第一種選擇,由 childViewController 自己負責,把自己給 dismiss 掉。代碼很簡單。

第二種選擇,由 parentViewController 負責,把 childViewController 給 dismiss 掉。代碼要複雜一些,分兩步。

childViewController 通知 parentViewController ,請求 dismiss 。

parentViewController 收到通知,執行 dismiss , childViewController 生命周期結束。

“第一種和第二種看似差別不大,而且第一種方法的代碼量很小。我們就采用第一種,忘掉第二種吧。”

别。千萬别。第一種雖然很多時候不緻于給你添 Error ,但是非常危險。你應該忘掉第一種,記住第二種。實際上,第一種做法是鑽了 Cocoa Touch 的空子,雖然偶爾能用而且不緻于添 Error ,但是非常不規範,應當摒棄。這兩種方式的差別,具體來說。

第一種方式,由 childViewController 自行 dismiss 掉自己,并不通知 parentViewController。這導緻 parentViewController 雖然擁有 present 下屬 childViewController 的權利,但一旦 present 結束,便完全失去了對 childViewController 控制。下一秒鐘, childViewController 是否仍然存在,它所含有的成員變量是否能夠被正常通路, parentViewController 将會一無所知。

大多數情況下, childViewController 完事後,需要和 parentViewController 進行溝通,在自己生命周期結束之際,将由自己負責采集、計算的資訊交給 parentViewController 保管(retain / copy)。很明顯,第一種方式完全無法實作這個“托管”的功能。

第二種方式其實有一個很正式的名字, Delegate Design Pattern in Objective-C 。沒錯,它隻是一種 Design Pattern ,是一種程式設計風格,并不是你在代碼裡必須要做的事情。但是這種程式設計風格實在是很實用,以至于被 Xcode 的官方模闆 Utility Application 所直接采用。 Delegate Design Pattern 看上去很複雜,實作起來其實不難,格式非常固定。我們看看 Utility Application 是怎麼做的。

這是 Utility Application 中 FlipsideViewController 對應的 .h 檔案。 FlipsideViewController 對應于我們上面描述裡的 childViewController 。

從上面的代碼裡,我們不難發現,在 childViewController 的層面上,實作 Delegate Design Pattern 的方法其實很固定,總結一下,有這麼幾個要點。

我們首先需要聲明一個協定,如上面代碼中的 FlipsideViewControllerDelegate 協定。有了這個協定,我們就能確定,負責 present 我們的 FlipsideViewController 的 parentViewController 一定會實作flipsideViewControllerDidFinish: 這個用來“臨終托管” FlipsideViewController 的方法。(否則編譯時我們會得到一個 Warning )

我們需要有一個 property 屬性為 assign 的指針。這個指針由 FlipsideViewController 所有,用來記錄自己的 parent 是誰。注意,屬性必須為 assign 而不能是 retain 或者 copy 。(否則。。你的記憶體管理會很難看。。)

剩下的事情就簡單了。當我們的 FlipsideViewController 被初次 alloc 和 init 後,将它的 delegate 指向将要 present 它的 parentViewController 。于此同時,我們需要確定 parentViewController 實作 flipsideViewControllerDidFinish: 函數,因為這個函數将是 FlipsideViewController 和 parentViewController “臨終通訊”的接口。然後,我們就沒事了。下一次 FlipsideViewController 生命周期将要結束時,它的 delegate ,也就是 parentViewController 将會及時收到通知,并負責儲存資訊和移除 FlipsideViewController 。借助 Delegate 風格的幫助,整個流程都将變得非常幹淨利索。