高效编写代码的方法(十九):Delegate要点
在OC中有很多方式可以进行对象间的“通信”,其中一种方式就是通过设置代理。
OC中使用代理的方式就不做过多介绍了。
重点介绍下使用代理需要注意或者可以提高的地方。
引用循环
可以参照官方的代理写法:
@property (nonatomic, weak, nullable) id <UITableViewDataSource> dataSource;
@property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate;
注意属性的修饰符是weak而不是strong,表示代理与委托之间的关系:
代理或者别的对象可能持有委托,而委托不持有代理。
声明为weak以防止引用循环。
内省判断
在真正执行代理方法之前,先判断delegate能否执行协议中声明的方法,一般如下判断:
if ([_delegate respondToSelector:@selector(someMethod:)]){
[_delegate someMethod:data];
}
这样判断的好处就是在于即时delegate没有实现方法,当delegate被触发想要执行时,也不会因为找不到方法而造成程序崩溃。
命名与参数
协议的方法命名除了要准确传达方法的意思(OC的传统)之外最好还能将所代理的对象作为参数写入命名中,比如一个数据加载器的协议方法:
- (void)networkFetcher:(NetworkFetcher *)fetcher
didReceiveData:(NSData *)data;
这样的好处在于delegate在执行的时候可以判断出不同的委托对象从而执行不同的方法:
- (void)networkFetcher:(NetworkFetcher *)fetcher
didReceiveData:(NSData *)data{
if(fetcher == _fetcherA){
//do A
}else{
//do B
}
定向
举个例子,应该就非常好理解了。
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
以上是一个WebView的原生代理,可以用来决定是否能发起某些特定的请求。
我们自己也可以多使用类似的代理方法进行某些定向问题的判断。
提供数据源
有时候,我们不止希望delegate能处理一些回调的逻辑,还希望delegate能给我们的委托对象提供一些数据源。此时我们就可以使用名为“dataSource”(自己命名也可以)的代理来处理数据部分的问题。最经常使用的就是在tableview中,我们使用dataSource来提供cell的数量以及cell。
使用delegate与dataSource分开的方式可以将逻辑与数据分开进行处理,显得比较清晰。
快速内省
一旦我们协议中有很多的代理方法时候,为了安全,我们每次执行都去进行一次respondToSelector的内省判断,那么整个过程就需要不断地重复写判断,显得非常麻烦。
有一种简单的方法就是定义一个结构体,然后用该结构体来保存所有的内省判断的结果。
举个例子:
@interface NetworkFetcher(){
struct {
unsigned int didReceiveData :1;
unsigned int didFailWithError:1;
unsigned int didUpdateProgress:1;
}_delegateFlag;
}
}
以上结构体_delegateFlag就以三个1字节的数据用来保存是否响应代理方法的Bool值。
在setDelegate:方法中我们进行统一判断和保存 ,如下:
- (void)setDelegate:(id<NetworkFetcher>)delegate{
_delegate = delegate;
_delegateFlag.didReceiveData = [_delegate respondToSelector:@selector(networFetcher:didReceiveData:)];
//省略
}
这样一来,在后续的判断中,我们可以直接使用_delegateFlag来判断代理能否响应方法。