在我們前幾天的學習中,已經認識了 struct 結構體、class 類別,還有能幫我們擴充功能的 extension。
今天,我們要讓這些工具之間「互相交流」,也就是要學 delegate(委派) 與 protocol(協議)!
protocol 可以想像成「規格書」或「合約」——它不會告訴你怎麼做,但會告訴你必須「會做什麼」。
任何 class、struct、enum 只要「簽了這份合約」,就必須實作裡面要求的功能。
protocol 協議名稱 {
    // 方法
    func 做某件事()
    
    // 屬性(可以讀寫或唯讀)
    var 名稱: String { get set }
}
protocol FruitDelivery {
    func deliverFruit(name: String)
}
struct AppleStore: FruitDelivery {
    func deliverFruit(name: String) {
        print("送出一箱 \(name) ")
    }
}
struct BananaStore: FruitDelivery {
    func deliverFruit(name: String) {
        print("送出一串 \(name) ")
    }
}
let appleShop = AppleStore()
appleShop.deliverFruit(name: "富士蘋果")
let bananaShop = BananaStore()
bananaShop.deliverFruit(name: "金香蕉")
🔍 解說
FruitDelivery 是協議protocol。AppleStore 和 BananaStore 都遵守它,所以都必須有 deliverFruit 這個方法。delegate 是一種「設計模式」,用來把某件事「交給別人做」,而這個「別人」就是代理人(delegate)。
這樣可以讓不同物件之間互相溝通,而不用緊密綁死彼此的程式碼。
protocol 協議名稱: AnyObject { // 限制只能給 class 用
    func 要代理做的事()
}
class 發送方 {
    weak var delegate: 協議名稱? // 防止循環引用
}
protocol DeliveryDelegate: AnyObject {
    func sendBanana(to place: String)
}
class AppleStore {
    //別忘記要設delegate變數!
    weak var delegate: DeliveryDelegate?
    
    func orderBanana() {
        print("AppleStore:我需要香蕉")
        delegate?.sendBanana(to: "蘋果商店")
    }
}
class BananaStore: DeliveryDelegate {
    func sendBanana(to place: String) {
        print("BananaStore:香蕉已送到 \(place)!")
    }
}
let appleStore = AppleStore()
let bananaStore = BananaStore()
appleStore.delegate = bananaStore
appleStore.orderBanana()
解說
DeliveryDelegate 是規範代理人必須會做的事(送香蕉)。AppleStore 有一個 delegate 屬性,可以指派給任何遵守這個協議的物件。BananaStore 符合 DeliveryDelegate,所以可以被指派成代理人。AppleStore 呼叫 delegate?.sendBanana,實際上是由 BananaStore 幫它送香蕉。當一個物件自己遵守協議並擔任自己的代理人時,可以直接用 self 來指定:
appleStore.delegate = self
這樣代表「我自己就是我的代理人」,不過通常會用在 UIViewController 內部,讓自己處理某些回呼事件。
self 在 delegate 中常用來表示「我自己就是代理人」。學會這個概念之後,你就能在 App 中建立物件和介面間的溝通橋樑,讓程式更靈活、更好維護!