委派模式是iOS和OS X开发中最常见的模式之一。 这是Apple框架大量使用的简单模式,即使是最简单的iOS应用程序也利用委派来完成其工作。 让我们先看一下委托的定义。
1.什么是授权?
定义
委托模式的定义简短而简单。 这就是Apple定义模式的方式。
委托是一个在另一个对象遇到程序中的事件时代表另一个对象或与另一个对象协同工作的对象。
让我们分解一下。 委托模式涉及两个对象,委托对象和委托对象。 例如, UITableView
类定义了将事件delegate
给其的delegate
属性。 委托属性需要符合UITableViewDelegate
协议,该协议在UITableView
类的头文件中定义。
在此示例中,表视图实例是委托对象。 委托通常是一个视图控制器,但它可以是符合UITableViewDelegate
协议的任何对象。 如果您不熟悉协议,则如果类实现了协议的必需方法,则它符合协议。 我们稍后再看一个示例。
当用户点击表视图中的一行时,表视图通过向其发送tableView(_:didSelectRowAtIndexPath:)
消息来通知其委托。 该方法的第一个参数是发送消息的表视图。 第二个参数是用户点击的行的索引路径。
表格视图仅将此事件通知其委托。 此类事件发生时,由代表决定需要做什么。 稍后您将学习到,这种职责分离是委派模式的主要好处之一。
优点
可重用性
委托具有多个优点,第一个优点是可重用性。 因为表视图将用户交互委托给它的委托,所以表视图不需要知道在点击其中一行时发生的情况。
换句话说,表视图可以不了解应用程序如何处理用户交互的实现细节。 将此职责委托给委托,例如视图控制器。
直接的好处是可以在大多数情况下按原样使用UITableView
类。 大多数情况下,无需子类化UITableView
即可使其适应您的应用程序需求。
松耦合
委托的另一个重要优点是松散耦合。 在有关单例的文章中 ,我强调应尽可能避免紧密耦合。 授权是一种积极促进松散耦合的设计模式。 那是什么意思
UITableView
类耦合到其委托进行工作。 如果没有委托与该表视图关联,则该表视图无法处理或响应用户交互。 这意味着需要一定程度的耦合。 但是,表视图及其委托是松散耦合的,因为实现UITableViewDelegate
协议的每个类都可以充当表视图的委托。 结果是一个灵活且松散耦合的对象图。
职责分离
委托的一个鲜为人知的优点是职责分离。 每当创建对象图时,重要的是要知道哪些对象负责哪些任务。 委托模式使这一点非常清楚。
对于UITableView
类,表视图的委托负责处理用户交互。 表格视图本身负责检测用户交互。 这是明确的职责分离。 这种分离使您作为开发人员的工作更加轻松和清晰。
2.例子
委托模式有几种风格。 让我们继续探索UITableViewDelegate
协议。
代表团
UITableViewDelegate
协议需要由表视图的委托实现。 表格视图通过UITableViewDelegate
协议通知其委托人有关用户交互的信息,但它也将委托人用于其布局。
Swift和Objective-C之间的重要区别是可以将协议方法标记为可选。 在Objective-C中,默认情况下需要协议的方法。 但是, UITableViewDelegate
协议的方法是可选的。 换句话说,一个类可能符合UITableViewDelegate
协议而无需实现该协议的任何方法。
但是,在Swift中,需要使用符合特定协议的类来实现协议定义的每个方法。 由于委托对象不需要验证委托是否实现了协议方法,因此这更加安全。 当我们实现委托模式时,本教程稍后将说明这种细微但重要的区别。
数据源
还有另一种与委托模式密切相关的模式,即数据源模式 。 UITableViewDataSource
协议是此模式的示例。 UITableView
类公开了类型为UITableViewDataSource
(在Objective-C中为id<UITableViewDataSource>
)的dataSource
属性。 这意味着表视图的数据源可以是实现UITableViewDataSource
协议的任何对象。
数据源对象负责管理作为其数据源的对象的数据源。 重要的是要注意,数据源对象负责保持对它公开给目标对象的项目的引用,例如表视图或集合视图。
例如,表视图向其数据源询问其需要显示的数据。 表格视图不负责保留其需要显示的数据对象。 该角色将移交给数据源对象。
数据源模式非常适合Model-View-Controller或MVC模式。 这是为什么? 例如,表视图是视图层的一部分。 它既不也不应该了解模型层,也不负责处理来自模型层的数据。 这意味着表视图的数据源或实现数据源模式的任何其他视图组件通常是某种控制器。 在iOS上,通常是UIViewController
子类。
数据源协议的方法签名遵循与委托协议相同的模式。 将消息发送到数据源的对象作为第一个参数传递。 数据源协议仅应定义与请求对象正在使用的数据相关的方法。
例如,表视图要求其数据源提供应显示的节和行的数量。 但是它还会通知数据源已插入或删除了行或节。 后者很重要,因为数据源需要更新自身以反映表视图中可见的更改。 如果表视图和数据源不同步,则会发生不良情况。
3.实施
物镜
现在我们了解了它的工作原理,实现委托模式非常简单。 看一下下面的Objective-C示例。
#import <UIKit/UIKit.h>
@protocol AddItemViewControllerDelegate;
@interface AddItemViewController : UIViewController
@property (weak, nonatomic) id<AddItemViewControllerDelegate> delegate;
@end
@protocol AddItemViewControllerDelegate <NSObject>
- (void)viewControllerDidCancel:(AddItemViewController *)viewController;
- (void)viewController:(AddItemViewController *)viewController didAddItem:(NSString *)item;
@optional
- (BOOL)viewController:(AddItemViewController *)viewController validateItem:(NSString *)item;
@end
我们声明一个类AddItemViewController
,它扩展了UIViewController
。 该类声明一个类型为id<AddItemViewControllerDelegate>
的属性delegate
。 请注意,该属性被标记为weak ,这意味着AddItemViewController
实例对其引用保持弱引用。
还要注意,我已经在UIKit框架的import语句下面添加了前向协议声明。 这是避免编译器警告所必需的。 我们可以将协议声明移到import语句下,但是我更喜欢将其放在类接口下。 这无非是个人喜好。
协议声明也非常简单。 AddItemViewControllerDelegate
协议扩展了NSObject
协议。 这不是强制性的,但将被证明非常有用。 我们稍后会找出原因。
AddItemViewControllerDelegate
协议声明了两个必需方法和一个可选方法。 正如我前面提到的,将委派对象作为每个委托方法的第一个参数传递,以告知委托哪个对象正在发送消息是一种好习惯。
必需的方法将事件,取消或添加通知给委托人。 可选方法要求委托人提供反馈。 它期望委托返回YES
或NO
。
这是代表团难题的第一步。 我们已经声明了一个类,该类声明了一个delegate
属性,并且已经声明了一个委托协议。 难题的第二部分是调用AddItemViewController
类中的委托方法。 让我们看看它是如何工作的。
在AddItemViewController
类的实现中,我们实现了cancel:
操作。 该动作可以连接到用户界面中的按钮。 如果用户点击按钮,则将事件通知给委托,结果,委托可以关闭AddItemViewController
实例。
- (IBAction)cancel:(id)sender {
if (self.delegate && [self.delegate respondsToSelector:@selector(viewControllerDidCancel:)]) {
[self.delegate viewControllerDidCancel:self];
}
}
建议验证委托对象不是nil
,并且它实现了我们将要调用的委托方法viewControllerDidCancel:
由于在NSObject
协议中声明了NSObject
respondsToSelector:
方法,因此这很容易。 这就是为什么AddItemViewControllerDelegate
协议扩展NSObject
协议的原因。 通过扩展NSObject
协议,我们可以免费获得此功能。
你可以省略为委托财产被检查nil
,因为respondsToSelector:
将返回nil
如果委托属性是nil
。 我通常会添加此检查,因为它可以清楚显示我们正在测试的内容。
第三个也是最后一个难题是委托对象对委托协议的实现。 下面的代码片段显示了AddItemViewController
实例的创建以及委托方法之一的实现。
- (IBAction)addItem:(id)sender {
// Initialize View Controller
AddItemViewController *viewController = [[AddItemViewController alloc] init];
// Configure View Controller
[viewController setDelegate:self];
// Present View Controller
[self presentViewController:viewController animated:YES completion:nil];
}
- (void)viewControllerDidCancel:(AddItemViewController *)viewController {
// Dismiss Add Item View Controller
...
}
不要忘记使充当AddItemViewControllerDelegate
协议委托的类符合如下所示。 您可以在类接口或私有类扩展中添加它。
#import "AddItemViewController.h"
@interface ViewController () <AddItemViewControllerDelegate>
@end
Swift
在Swift中,委派模式很容易实现,您会发现Swift使委派略显优雅。 让我们在Swift中实现上述示例。 这就是Swift中AddItemViewController
类的样子。
import UIKit
protocol AddItemViewControllerDelegate: NSObjectProtocol {
func viewControllerDidCancel(viewController: AddItemViewController)
func viewController(viewController: AddItemViewController, didAddItem: String)
func viewController(viewController: AddItemViewController, validateItem: String) -> Bool
}
class AddItemViewController: UIViewController {
var delegate: AddItemViewControllerDelegate?
func cancel(sender: AnyObject) {
delegate?.viewControllerDidCancel(self)
}
}
在Swift中,协议声明看起来有些不同。 请注意, AddItemViewControllerDelegate
协议扩展了NSObjectProtocol
而不是NSObject
协议。 在Swift中,类和协议不能使用相同的名称,这就是为什么NSObject
协议在Swift中使用不同名称的原因。
delegate
属性是AddItemViewControllerDelegate?
类型的变量AddItemViewControllerDelegate?
。 请注意协议名称末尾的问号。 委托属性是可选的。
在cancel(_:)
方法中,我们调用viewControllerDidCancel(_:)
委托方法。 单行显示了Swift的优雅程度。 在调用委托方法之前,我们安全地打开delegate
属性。 无需检查委托是否实现了viewControllerDidCancel(_:)
方法,因为Swift中需要协议的每个方法。
现在让我们看一下ViewController
类,该类实现AddItemViewControllerDelegate
协议。 该接口向我们展示了ViewController
类扩展了UIViewController
类并采用了AddItemViewControllerDelegate
协议。
import UIKit
class ViewController: UIViewController, AddItemViewControllerDelegate {
func addItem(send: AnyObject) {
// Initialize View Controller
let viewController = AddItemViewController()
// Configure View Controller
viewController.delegate = self
// Present View Controller
presentViewController(viewController, animated: true, completion: nil)
}
func viewControllerDidCancel(viewController: AddItemViewController) {
// Dismiss Add Item View Controller
...
}
func viewController(viewController: AddItemViewController, didAddItem: String) {
}
func viewController(viewController: AddItemViewController, validateItem: String) -> Bool {
}
}
在addItem(_:)
方法中,我们初始化AddItemViewController
类的实例,设置其delegate
属性,并将其呈现给用户。 请注意,我们已经实现了AddItemViewControllerDelegate
协议的每个委托方法。 如果不这样做,编译器将告诉我们ViewController
类不符合AddItemViewControllerDelegate
协议。 通过注释掉其中一种委托方法来进行尝试。
结论
委托是开发iOS和OS X应用程序时经常遇到的一种模式。 可可在很大程度上依赖于这种设计模式,因此熟悉它很重要。
自从几年前引入块以来,Apple一直在向某些委派实现缓慢提供基于块的替代API。 一些开发人员通过提供自己的基于块的替代方案来跟随Apple的领导。 例如,流行的AFNetworking库严重依赖于块而不是委派,从而产生了一种优雅,直观的API。
翻译自: https://code.tutsplus.com/articles/design-patterns-delegation–cms-23901