委託模式

一個代表是可可和 CocoaTouch 框架使用了常見的設計模式,在實現一些功能到另一個類代表的責任。這遵循關注點分離的原則,其中框架類實現通用功能,而單獨的委託例項實現特定用例。

檢視委託模式的另一種方法是在物件通訊方面。Objects 經常需要相互交談,並且這樣做一個物件需要符合 protocol 才能成為另一個 Object 的委託。完成此設定後,另一個物件會在有趣的事情發生時與其代理進行對話。

例如,使用者介面中用於顯示資料列表的檢視應僅負責資料顯示方式的邏輯,而不是用於決定應顯示哪些資料。

讓我們深入研究一個更具體的例子。如果你有兩個類,父母和孩子:

class Parent { }
class Child { }

並且你想要通知父母孩子的更改。

在 Swift 中,代表是使用 protocol 宣告實現的,因此我們將宣告 protocol 將實現的 protocol。這裡代表是 parent 物件。

protocol ChildDelegate: class {
    func `childDidSomething()`
}

子程序需要宣告一個屬性來儲存對委託的引用:

class Child {
    weak var delegate: ChildDelegate?
}

注意變數 delegate 是可選的,協議 ChildDelegate 被標記為僅由類型別實現(沒有這個 delegate 變數不能被宣告為 weak 參考,避免任何保留週期。這意味著如果 delegate 變數不再在其他地方引用,它將被釋放)。這樣父類只在需要和可用時才註冊委託。

另外,為了將我們的代理標記為 weak,我們必須通過在協議宣告中新增 class 關鍵字來約束我們的 ChildDelegate 協議以引用型別。

在這個例子中,當孩子做某事並需要通知其父母時,孩子會呼叫:

delegate?`.childDidSomething()`

如果已定義代理,則將通知代理該孩子已做某事。

父類需要擴充套件 ChildDelegate 協議才能響應其操作。這可以直接在父類上完成:

class Parent: ChildDelegate {
    ...

    func `childDidSomething()` {
        print("Yay!")
    }
}

或者使用副檔名:

extension Parent: ChildDelegate {
    func `childDidSomething()` {
        print("Yay!")
    }
}

父母還需要告訴孩子這是孩子的代表:

// In the parent
let child = `Child()`
child.delegate = self

預設情況下,Swift protocol 不允許實現可選功能。只有在你的協議標有 @objc 屬性和 optional 修飾符時,才能指定這些。

例如,UITableView 實現了 iOS 中表檢視的通用行為,但是使用者必須實現兩個名為 UITableViewDelegateUITableViewDataSource 的委託類,它們實現特定單元格的外觀和行為方式。

@objc public protocol UITableViewDelegate : NSObjectProtocol, UIScrollViewDelegate {
        
        // Display customization
        optional public func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath)
        optional public func tableView(tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int)
        optional public func tableView(tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int)
        optional public func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath)
        ...
}

你可以通過更改類定義來實現此協議,例如:

class MyViewController : UIViewController, UITableViewDelegate

必須實現在協議定義中未標記 optional 的任何方法(在本例中為 UITableViewDelegate)。