在iOS開發中,物件之間的通信至關重要,Delegate和Protocol正是實現這一目的的核心工具。這些概念初學者可能會感到陌生,覺得這些概念有點難以掌握,但理解它們後,你會發現它們不僅簡單實用,還能大幅提升應用程序的彈性和可維護性。
在第13天的文章中,我們探討了Unwind Segue作為一種資料傳遞的方式,能有效地返回上一個視圖控制器並傳遞資料。然而,Delegate和Protocol提供了更加靈活和強大的解決方案,尤其是在需要多個對象之間進行互動時。這篇文章將通過領包裹的案例來深入分析單人通訊的情境,幫助你更清楚地理解這些概念的實際應用。
在 iOS 開發中,Delegate 和 Protocol 是實現物件之間通信的核心機制。它們常常一同使用,協助不同的物件進行協同作業,而不需要強耦合。簡單來說,Delegate 讓一個對象可以將某些責任「委派」給另一個對象來完成,而 Protocol 則是一組規則,定義了應該實現的功能。
舉例來說:物件之間的通信和互動
// 定義一個委派協議
protocol MyDelegate: AnyObject {
func didSomething()
}
// 類別允許使用 MyDelegate 協議作為委派
class MyClass {
weak var delegate: MyDelegate?
func performTask() {
// 做一些事情
// 通知委派
delegate?.didSomething()
}
}
// 實現委派協議的類別
class AnotherClass: MyDelegate {
func didSomething() {
print("AnotherClass did something.")
}
}
// 使用
let myObject = MyClass()
let anotherObject = AnotherClass()
// 將 AnotherClass 設定為 MyClass 的委派
myObject.delegate = anotherObject
// MyClass 執行任務,會通知委派
myObject.performTask()
例子:領包裹通知
讓我們使用一個領包裹通知的例子來演示如何使用委派模式。這個例子涉及到守衛(SecurityGuard)、住戶(Resident)和領包裹通知(PackageDeliveryNotification)。
// 定義領包裹通知協議
protocol PackageDeliveryNotification: AnyObject {
func notifyPackageReceived()
}
// 住戶類別 加入protocol,這樣守衛通知你就收的到
class Resident: PackageDeliveryNotification {
let name: String
init(name: String) { self.name = name }
func notifyPackageReceived() {
print("\(name) received a package notify. 📦")
}
}
// 守衛類別 加入protocol,負責發出通知
class SecurityGuard {
weak var deliveryDelegate: PackageDeliveryNotification?
func deliverPackage() {
// 守衛發送通知
print("Security guard notifies residents about a delivered package.")
// 通知住戶
deliveryDelegate?.notifyPackageReceived()
}
}
// 使用
let resident1 = Resident(name: "Alice")
let resident2 = Resident(name: "Bob")
let securityGuard = SecurityGuard()
// 將住戶設定為守衛的委派
securityGuard.deliveryDelegate = resident1
// 守衛送包裹,會通知住戶
securityGuard.deliverPackage()
// 將住戶更改為另一位
securityGuard.deliveryDelegate = resident2
// 守衛再次送包裹,會通知新的住戶
securityGuard.deliverPackage()
有點文縐縐的。一般來說,學到這段時,通常腦袋會打結,所以一定要用自己的話再說一次。
好,我們來聊聊會讓我們思維亂掉的地方 deliveryDelegate
deliveryDelegate 是代理的意思,但又是 protocol 協議型別? 最後卻將住戶指定給它。
是指有二個物件,因為有個事件發生,他們要互動。會有
定義協議(protocol)和協議中的功能名稱。
任何希望收到通知的對象(物件B)都需要安裝這個協議(protocol),實作裡面的功能名稱和內容。
發出通知的對象(物件A)登記指定(物件B)。當事件發生時,(物件A)執行(物件B)協議中的功能名稱。
定義協議(protocol)和協議中的功能名稱。
我們大樓的保全公司他們是用『智生活APP』來提供領包裹通知服務,所以protocol 的名稱叫 『智生活APP』,他裡面有 『領包裹通知() 』的服務
當然這裡的APP 你可以換成你們常用的如Line也行。
所以這段 protocol PackageDeliveryNotification 程式你可以這麼理解
// 定義領包裹通知協議
protocol 智生活APP: AnyObject {
func 領包裹通知()
}
住戶要安裝 『智生活APP』,裝好後,有包裹事件時,守衛就會直接執行住戶的 『智生活APP』的『領包裹通知()』。
所以住戶要實作『領包裹通知()』功能,裡面要寫住戶要如何處理這個事件,例如準備磁扣到櫃檯領包裹
// 住戶類別 加入protocol,這樣守衛通知你就收的到
class 住戶: 智生活APP {
let name: String
init(name: String) { self.name = name }
func 領包裹通知() {
print("準備磁扣到櫃檯領包裹. 📦")
}
}
但是有可能住戶沒裝APP,就會變成
// 住戶類別 你是不是忘了加入protocol,這樣守衛通知領包裹,你是收不到的唷
class 住戶 {
let name: String
init(name: String) { self.name = name }
func 領包裹通知() {
print("準備磁扣到櫃檯領包裹. 📦")
}
}
或是這樣,住戶沒裝APP,也不知道可以領包裹
// 住戶類別 你是不網購的人,我了解
class 住戶 {
let name: String
init(name: String) { self.name = name }
}
發出通知的對象(物件A)登記指定(物件B)。當事件發生時,(物件A)執行(物件B)協議中的功能名稱。
要請住戶來登記,而且要請住戶裝 『智生活APP』
如果有包裹到,就執行這個住戶安裝『智生活APP』的 『領包裹通知()』
因為這個住戶他可能沒裝 『智生活APP』就來登記, 所以住戶後面要加問號表示有沒裝APP的可能。萬一執行了,就算他沒裝APP也沒關係,就只是單純住戶收不到通知而已,不會卡關,反正我通知了,裝不裝APP是住戶的事,我有請你要裝APP了。
// 守衛類別 加入protocol,負責發出通知
class 守衛 {
weak var 登記的住戶: 智生活APP?
func 領包裹通知() {
// 守衛發送通知
print("保全人員通知居民包裹已送達。")
// 通知住戶,用 智生活APP 的 領包裹通知()
登記的住戶?.領包裹通知()
}
}
經過 1, 2, 3 步驟後,整個宣告工作就準備完成了。
接下來就是實際流程了。
let resident1 = 住戶(name: "Alice")
let securityGuard = 守衛()
securityGuard.登記的住戶 = resident1
securityGuard.領包裹通知()
class 住戶沒裝APP {
let name: String
init(name: String) { self.name = name }
func 領包裹通知() {
print("準備磁扣到櫃檯領包裹. 📦")
}
}
let resident3 = 住戶沒裝APP(name: "Jason")
let securityGuard2 = 守衛()
securityGuard.登記的住戶 = resident3
發生錯誤
按了 Fix 後,會自動加上 func 領包裹通知() ,請你實作。
整理一下中文版的程式 XD
protocol 智生活APP: AnyObject {
func 領包裹通知()
}
class 住戶: 智生活APP {
let name: String
init(name: String) { self.name = name }
func 領包裹通知() {
print("準備磁扣到櫃檯領包裹. 📦")
}
}
class 守衛 {
weak var 登記的住戶: 智生活APP?
func 領包裹通知() {
print("保全人員通知居民包裹已送達。")
登記的住戶?.領包裹通知()
}
}
let resident1 = 住戶(name: "Alice")
let securityGuard = 守衛()
securityGuard.登記的住戶 = resident1
securityGuard.領包裹通知()
就這樣,透過生活上的例子來對比,應該可以比較好記一下。
如果沒有住戶沒有裝 App ,沒有代理人裝的APP的幫忙,守衛就要自己去完成 領包裹通知() 裡的所有動作,現在只要交給住戶安裝的 App 去通知就好。一般在信箱上放牌子,也算是一種代理,只是沒那麼即時,但守衛一樣放完牌子就沒事了。
在實際應用中,Delegate 和 Protocol 被廣泛用於許多常見的 UIKit 元件。例如:
UITableViewDataSource – 表格視圖的資料來源協議,用於定義表格的資料呈現方式。
UITableViewDelegate – 表格視圖的委派協議,用於處理表格中的行選擇、編輯等互動。
UITextFieldDelegate – 文字輸入框的委派協議,用於處理輸入框的編輯行為(如開始或結束編輯、驗證內容等)。
這些例子展現了 delegate 和 protocol 的強大靈活性,允許我們在應用中處理複雜的互動邏輯,同時保持結構的清晰和可維護性。
使用 Delegate 和 Protocol 可以讓應用程式的結構更具彈性和可維護性。這些技術使得物件之間能夠進行更加靈活的通信,而不需要彼此間的強耦合。透過包裹通知的範例,我們可以更清晰地理解這些概念,儘管這個例子,登記的住戶要換來換去不斷變動,很顯然不太符合生活的情況,但它展示了委派機制的運作原理。明天,我們將進一步探討多個物件之間的群體通信,並介紹更多實際應用情境。