# 23、通过委托与数据源协议进行对象间通信

“委托模式”/“代理模式”（Delegate pattern）主旨：

​ 定义一套接口，其他对象若想接收当前对象的委托，则需遵从此接口成为其“委托对象”（delegate）。而当前对象则可以给其委托对象传递一些信息，也可以在放生相关事件时通知委托对象。（此模式可将数据与业务逻辑解耦）

​ 通常情况下`delegate`对象会持有当前对象，所以需要将`delegate`属性定义成`weak`，否则会造成循环引用导致内存泄露。

​ 若要向外界公布此类实现了某协议，那么就在接口（.h文件）中声明；而如果这个协议是个委托协议的话，就可以在类内部（class-continauation分类中）声明。

​ 委托协议中的方法一般都是“可选的”（用`@optional`声明，后面的都是可选），因为代理未必关心其中的所有方法。

​ 在调用delegate中的方法时，总是应该把当前对象也一并传入方法中，这样`delegate`在实现相关方法时，就能根据传入的实例分别执行不同的代码了。如：

```objectivec
- (void)networkFetcher:(NetworkFetcher *)fetcher 
              didReceiveDate:(NSData *)data {
  if (fetcher == _myFetcherA) {
  } else if (fetcher == _myFetcherB) {
  }
}
```

​ 可以在当前对象中声明一个含有位段的结构体为其实例对象，结构体中的每个位段表示`delegate`是否实现了协议中的相关方法：

```objectivec
@interface NetworkFetcher () {
  struct {
    unsigned int didReceiveData : 1;
    unsigned int didFailWithError : 1;
    unsigned int didUpdateProgressTo : 1;
  } _delegateFlages; // 实例对象!!!
}
// 设置代理时，就缓存当前delegate是否能响应协议中的相关方法
- (void)setDelegate:(id< NetworkFetcher>)delegate {
  _delegate = delegate;
  _delegateFlages.didReceiveData = [delegate respondsToSelector:@selector(networkFetcher:didReceiveData:)];
  ....
}
```

​ 协议方法要调用很多次时，值得进行这种优化。而是否需要优化，则应依照具体代码来定。这种需要分析代码性能，并找出瓶颈，若发现执行速度需要改进，则可使用此技巧。

要点：

* 委托模式为对象提供了一套接口，使其可由此将相关事件告知其他对象
* 将委托对象应该支持的接口定义成协议，在协议中把可能需要处理的时间定义成方法
* 当某对象需要从另外一个对象中获取数据时，可使用委托模式。这种情境下，该模式亦称“数据源协议”（data source protocal）
* 若有必要，可实现含有段位的结构体，将委托对象是否能响应相关协议方法这一信息缓存至其中
