委托模式

一个代表是可可和 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)。