iOS 開發者指南:透過 Swift 4 學習 Delegates 與 Delegation


本文主要講述 “delegates” 與 “delegation”。利用本文提供的完整專案源碼,我們將做一個簡單示例,在 Swift 4 中實現 delegation 設計模式。我將展示 delegation 的操作方式,讓你不再在複雜的專案中陷入困境。為了讓你成為頂尖開發者,我將會介紹最棒的設計工具 UML,以協助物件導向軟件開發。文中所展示的 UML 圖,設計並記錄了專案範例中使用 delegation 設計模式的實作。

我將展示如何構建一個 user interface (UI) helper,這是一個在指定 URL 下載文件的類別。最重要的是,文中會展示在圖像文件已完成下載後,UI helper 如何通過 delegation 通知 UIViewController 子類別,讓視圖控制器可在螢幕上顯示圖像。為了簡單明瞭,我們假設 Swift 對於從 URL 下載檔案提供最小支持。我們將使用 delegation 設計模式手動連接檔案已完成下載的通知。以下是我們將構建的應用程式:

Swift Delegate Demo

推薦閱讀

為了解我如何創建文中的 delegation 範例程式碼,建議你閱讀以下文章:

Delegation

讓我們先了解術語的定義。

劍橋字典 (Cambridge dictionary) 定義的 “delegate” 是表示「將特定的工作、職責、權利等給予別人,讓他們為你完成。」韋伯字典 (Merriam-Webster dictionary) 定義的 “delegation” 是指「授權給另一方行事的行為」。

簡而言之,「Delegation 是一種設計模式,使 class 和 structure 能夠將其某些職責交給(或委託)其他類型的物件。」

你們都可能使用過 delegation,它很常在 iOS 的應用程式中出現。

思考一下,一個 UIViewController 的子類別 (命名為 ViewController) 來管理 UICollectionViewViewController 包含一個 UICollectionView 物件 (命名為 collectionView),你將 collectionView 實例的 delegate 屬性設為 selfself 就是 ViewController

ViewController 採用 UICollectionViewDelegate 的 protocol (協議),delegate 就是 ViewController。為了符合 UICollectionViewDelegate 的 protocol,ViewController 實現像 collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 的方法。

通過實現 didSelectItemAt 方法,UICollectionViewCell 物件被點擊時,ViewController 就會得到通知。當一個 cell 被點擊時,ViewController 實現的 didSelectItemAt 方法將被調用。你可以定義 didSelectItemAt 方法的實作內容,舉例來說,在點擊某個 cell 時,可以改變該 cell 的外觀,並執行一些特定的邏輯,collectionView 將處理點擊UICollectionViewCell 物件的責任委託ViewController

以下是 delegation 和 UICollectionViewDelegate 的範例 (點擊連接)。

引述 Apple 官方說明:

Delegation is a simple and powerful pattern in which one object in a program acts on behalf of, or in coordination with, another object. The delegating object keeps a reference to the other object–the delegate–and at the appropriate time sends a message to it. The message informs the delegate of an event that the delegating object is about to handle or has just handled. The delegate may respond to the message by updating the appearance or state of itself or other objects in the application, and in some cases it can return a value that affects how an impending event is handled. The main value of delegation is that it allows you to easily customize the behavior of several objects in one central object. …

以及:

A delegate is an object that acts on behalf of, or in coordination with, another object when that object encounters an event in a program. The delegating object is often a responder object–that is, an object inheriting from NSResponder in AppKit or UIResponder in UIKit–that is responding to a user event. The delegate is an object that is delegated control of the user interface for that event, or is at least asked to interpret the event in an application-specific manner. …

The programming mechanism of delegation gives objects a chance to coordinate their appearance and state with changes occurring elsewhere in a program, changes usually brought about by user actions. More importantly, delegation makes it possible for one object to alter the behavior of another object without the need to inherit from it. The delegate is almost always one of your custom objects, and by definition it incorporates application-specific logic that the generic and delegating object cannot possibly know itself. …

必要條件:protocol

為了編寫實現 delegation 設計模式的範例代碼,我需要一個 “protocol”。請參閱我這篇關於 protocol 的文章,並記住如蘋果官方所說(粗體為重點補充):

Delegation is a design pattern that enables a class or structure to hand off (or delegate) some of its responsibilities to an instance of another type. This design pattern is implemented by defining a protocol that encapsulates the delegated responsibilities, such that a conforming type (known as a delegate) is guaranteed to provide the functionality that has been delegated. Delegation can be used to respond to a particular action, or to retrieve data from an external source without needing to know the underlying type of that source.

蘋果官方解釋:

A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol. …

程式碼講解

請注意:下文將用到的一些非常重要的術語詞彙與定義,如有需要,可參閱前文的段落以及引述的解釋。

委託對象 (the delegating object)

我將構建一個實作圖片下載器的類別 LogoDownloader,這個類別只要有一個特定的 URL 就可以異步下載檔案,並在下載完成時提供通知。這就是「委託對象」,如上文定義,即「保留對其他物件——代理對象——的引用,並在適當的時候向其發送消息。」

委託協議 (the delegate protocol)

一個 Protocol 是需要被定義的。請記住,「delegation 設計模式是通過定義一個封裝了委託職責的 protocol 來實現的,一個符合的類型(被稱為 delegate)就能保證提供被委託的功能」(粗體為重點補充)。它將是 LogoDownloaderDelegate 的 protocol。

代理對象 (the delegate)

我將實作一個繼承 UIViewController 的子類,並使其符合 LogoDownloaderDelegate delegate protocol (委託協議)。請記住,delegation 設計模式是通過定義一個封裝委託職責的 protocol 來實現的,符合的類型(稱為 delegate)能夠提供已被委託的功能,我將調用這個類別 ViewController

UML:這些角色 (Piece) 如何融合在一起

在進入專案程式碼之前,我繪製了一個 UML 圖。在設計物件導向軟體開發方向,UML 是我用過最好的工具。看一下我的圖表及參考備註,並閱讀下列 UML 的參考資料:連結一連結二連結三,以及連結四

UML Diagram

Swift Delegate Diagram

Code demo

這就是範例應用程式所呈現的畫面:

Swift Delegate Demo

編寫程式碼

委託對象 (the delegating object)

委託對象 LogoDownloader 可以下載(圖片)檔案,讓我們看一下它的程式碼。我對 delegate 物件(LogoDownloaderDelegate 類型)採取 optional 引用,允許 LogoDownloader 類別實例在沒有 delegate 時也能進行操作,如果 delegate 成員變數是 nilLogoDownloader 仍會下載檔案並調用自己的 didDownloadImage() 方法。

以下是 LogoDownloader.swift 的程式碼——注意 optional(?) 的 “delegate” 屬性,並留意 delegate 變數向哪裡發送 delegate?.didFinishDownloading(self) 消息:

請注意: 從技術上講,我應該將 delegate 的宣告標記為:

藉此來避免 retain cycles,注意,這裡的 LogoDownloaderDelegate 只是一個 Protocol。雖然理解 delegate 本就是相當困難的,在試圖理解 delegate 時,我不想讓你更加困惑,但我不得不在此介紹另一個概念。

如果我將 delegate 宣告為 weak,將會收到錯誤消息,“‘weak’ may only be applied to class and class-bound protocol types, not ‘LogoDownloaderDelegate’”。

你可以在我的這篇教程 “Tutorial: delegates and delegation in Objective-C” 中看到,在使用 Objective-C 開發時,宣告為 weak delegate 的限制相對寬容。

委託協議 (the delegate protocol)

LogoDownloaderDelegate 是 delegate protocol (委託協議),你可以將 protocol 的概念化為契約 (contract)承諾 (promise),可應用於 class、structure 或 enumeration。下面我會將 ViewController 帶入一個 LogoDownloaderDelegate 協議的 contract 中,ViewController承諾實現這個契約,實作 LogoDownloaderDelegate 要求實體或履行的內指定方法與成員變數。

以下為 LogoDownloaderDelegate.swift 程式碼:

練習:

我為何要在 didFinishDownloading(_) 宣告函式中指定對 LogoDownloader 物件的引用?

代理對象 (the delegate)

我的 ViewController 接受——符合——LogoDownloaderDelegate 這個委託協議,注意:

1) 它有一個 optional (?)LogoDownloader 實例 (var logoDownloader:LogoDownloader?);

2) 我使用 optional chaining (可選鏈),「Optional Chaining 是一個請求和呼叫屬性、方法及子腳本的過程,它的可選性當前可能為空 (nil)。如果可選的目標有數值,就會成功呼叫屬性、方法或子腳本;相反,如果選擇的目標為空 (nil),呼叫將回傳空 (nil)。多次請求或呼叫可以被鏈接形成一個鏈,如果任何一個節點為空 (nil) 將導致整個鏈失效。」

3) 它將 LogoDownloader 實例的成員變量 delegate 設置為self

4) 它提供 LogoDownloader自定義操作,允許我在呼叫這些協議方法時執行任何操作,例如完成下載時驅動圖像顯示。

以下為 ViewController.swift 程式碼:

總結

希望你能夠徹底閱讀和消化關於 delegation 的初步討論,可參考文中分享的連結,並閱讀推薦文章。請注意,文章沒有解釋每一個細節,在查看程式碼時,希望你多作思考。如果讀者不了解某個概念,請在參考資料或其他資訊中查找相關內容,編寫出你自己的 delegation code。

最重要的是,不斷練習,取得經驗——並請享受整個過程!

譯者簡介:陳奕先-過去為平面財經記者,專跑產業新聞,2015年起跨進軟體開發世界,希望在不同領域中培養新的視野,於新創學校ALPHA Camp畢業後,積極投入iOS程式開發,目前任職於國內電商公司。聯絡方式:電郵[email protected]

FB : https://www.facebook.com/yishen.chen.54
Twitter : https://twitter.com/YeEeEsS

原文Understanding Delegates and Delegation in Swift 4


熱愛寫作的多產作家,亦是軟體工程師、設計師、和開發員。最近專注於 Objective-C 和 Swift 的 iOS 手機 App 開發。但對於 C#、C++、.NET、JavaScript、HTML、CSS、jQuery、SQL Server、MySQL、Oracle、Agile、Test Driven Development、Git、Continuous Integration、Responsive Web Design 等。

blog comments powered by Disqus
訂閲電子報

訂閲電子報

AppCoda致力於發佈優質iOS程式教學,你不必每天上站,輸入你的電子郵件地址訂閱網站的最新教學文章。每當有新文章發佈,我們會使用電子郵件通知你。

已收你的指示。請你檢查你的電郵,我們已寄出一封認證信,點擊信中鏈結才算完成訂閱。

Shares
Share This