注意事项
在上面的范例中,我们看到了设计delegate 与protocol 应该注意的地方:
Delegate 不应该指定Class
我们将delegate物件定义成id <MyButtonDelegate> delegate
,意思就是不需要管这个实例属于哪个class,只要是个Objective-C实例即可,但是这个实例必须遵守MyButtonDelegate
protocol。
我们其实可以将delegate实例是那个class写死,例如把MyButton
的delegate的class指定成MyController
,但这样做非常不好,如此一来,就只有MyController
可以使用MyButton
,其他controller都无法使用,就大大减少了重复使用MyButton
的弹性。
Delegate 这种设计方式,也方便我们在同时开发Mac OS X 与iOS跨平台项目时共用代码,我们在撰写某个model 实例的时候,只使用Foundation 或是其他两个平台都有的framework,至于与平台相依的部份,就放进delegate 中,然后在Mac OS X 与iOS 上各自作现delegate 方法。
总之,在实现delegate的时候,delegate属于哪个class并不重要,重要的是delegate有没有实现我们想要调用的method。
Delegate 属性应该要用Weak,而非Strong
在使用property 语法的时候,如果这个property 是Objective-C实例,我们照理说应该要设定成strong 或retain,但是遇到的是delegate,我们应该设成weak 或assign。
原因是:需要设计delegate的这个实例,往往是其delegate实例的成员变量。在我们的例子中,MyButton
的instance是myButton
,是MyController
的成员变量,自己可能已经被MyController
retain了一份。如果MyButton
又retain了一次MyController
,就会出现循环retain的问题—我已经被别人retain,我又把别人retain一次。
如此,会造成我们会无法释放MyController
:在该释放MyController
的时候,MyController
还是被自己的成员变量retain,MyController
得要走到dealloc
才会释放myButton
,但是自己却因为被myButton
给retain起来,而始终走不到dealloc
。
Delegate Method 的命名方式
Delegate method的命名有个鲜明的特色,就是这个method至少会传入一个参数,就是把到底是谁呼叫了这个delegate method传递进来。同时,这个method也往往以传入的class名称开头,让我们可以辨识这是属于哪个class的delegate method。以UITableViewDelegate
为例,假如我们在iOS的表格中,选择了某一列,就会调用
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Method 的名称就以「tableView」开头,让我们知道这是属于Table View 的delegate method,然后第一个参数把这个Table View 的instance传入,接下来才传入到底是哪一列被选起来的信息。
至少把是谁调用了这个delegate method传入的理由很简单。以我们的MyController
为例,这个controller可能有好几个MyButton
,而这些MyButton
全都把delegate指到同一个controller上,那么,controller就需要知道到底是被哪个button呼叫。判断方式只要简单比对指标就好了:
- (void)myButtonDidBecomeQuadrupleClicked:(MyButton *)button
{
if (button == myButton1) {
}
else if (button == myButton2) {
}
}