0 关键词
代理模式、delegate
1 概述
Delegate是Cocoa的精髓之一,Delegate在Cocoa及各大框架下出现频率极高,很少有框架像Cocoa这样对代理模式如此痴迷,因此Delegate在iOS/MacOS开发中的重要性不言而喻。不过近年来Block在官方新API出现的频率明显增多,Block取代Delegate完成了不少功能。但Delegate仍然大量存在于Cocoa框架之中。
2 Delegate的声明步骤
完整的代理声明步骤如图所示,左侧是委托方(如UITableView),右侧是代理方(如UIViewController或UITableViewController)。
Delegate声明步骤
3 使用Delegate的常见问题
3.1 代理不执行
- 检查是否指定的代理人。如在ViewController中检查是否指定了TableView的代理
_tableView.delegate = self;
。这是很多人都会忘记的一步,当代理不执行时才会想起来还没有指定代理。
如何避免忘记指定代理
我个人一般会在添加子视图的时候指定代理,代码写法如下:
//Code in ViewController
[self.view addSubview:({
_tableView = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStyleGrouped];
_tableView.dataSource = self;
_tableView.delegate = self;
_tableView;
})];
()是什么
GNU C支持一种叫作Statements and Declarations in Expressions(怎么翻译?)的语法形式,可以在小括号内写多行代码,最后一行必须为返回值。这种写法在GCC和LLVM下都可编译通过。如:
({ int y = foo (); int z;
if (y > 0) z = y;
else z = - y;
z; })
#define max(a,b) ((a) > (b) ? (a) : (b))
原文出处 Statements and Declarations in Expressions
- 检查声明delegate的六步骤中的其余五步是否有遗漏。
- 在特定情况下,部分代理方法确实不会执行。常见的比如UITableView的
tableView:numberOfRowsInSection:
返回0时,tableView:cellForRowAtIndexPath:
协议方法不会执行。
3.2 通过恰当的内存语义设置,避免引用循环
代理对象不拥有委托对象,所以属性在ARC下使用weak修饰,在MRC下使用assign修饰(现在估计很少有人使用MRC了吧)。
3.3 由代理引起的Crash
- 出现unrecognized selector sent to instance 情况一
出现该问题是因为指定了代理人,但该代理人未实现协议方法。解决方案一般从两方面入手:
一是,编译器提示。使用@requested和@optional区别哪有方法必须实现,哪些可以不实现。
二是,添加防御性的自省代码。这样即便未实现方法,也不会crash。
如
if (self.delegate && [self.delegate respondsToSelector:@selector(didClickButton:)]) {
[self.delegate didClickButton:button];
}
*出现 unrecognized selector sent to instance情况二
出现该问题的原因是代理对象被释放了之后委托对象又向被释放了的代理对象发送了消息。在MRC中,代理对象被设置为assign,当委托对象被释放掉之后,delegate的指针并没有被置空。所以需要在合适的时间点(例如dealloc方法内)将delegate设置为nil。在ARC中,代理对象被设置为weak,委托对象被释放时,用weak修饰的属性会自动置nil,所以不会crash。
网友评论