前言:在实现代理模式时,如果协议中的方法是可选的,那么就会写出一大批类似于这样的代码:
if ([_delegate respondsToSelector:@selector(fetchStart:alwaysLogString:)]) { [_delegate fetchStart:self alwaysLogString:@"alwaysLog"]; }
很容易用代码查出某个委托对象是否能响应特定的选择子,可是如果频繁的执行此操作的话,那么除了第一次检测的结果有用之外,后续的检测可能都是多余的。如果委托对象本身没变,那么不太可能会突然响应某个原来不能相应的方法,也不太会突然无法响应某个原来可以响应的方法。因此我们通常把委托对象能否响应某个协议方法这一信息缓存起来,以优化程序效率。
将方法响应能力缓存起来的最佳途径就是使用“位段”数据类型。我们可以把结构体中某个字段所占用的二进制位个数设定为特定的值。比如这样:
struct { unsigned int fieldA :8; unsigned int fieldA :4; unsigned int fieldA :2; unsigned int fieldA :1; }
在结构体中,fieldA位段将占用8个二进制位,fieldB占用4个,fieldC占用两个,fieldD占用一个。于是,fieldA可以表示0至255之间的值,而fiedD则可以表示0或1这两个值。我们可以像fieldD这样把委托对象是否实现了协议中的相关方法这一信息缓存起来。如果创建的结构体中只有大小为1的位段,那么就能把许多Boolean值塞入一小块数据里面了。我们可以在实例中嵌入一个含有位段的结构体作为其实例变量,而结构体中的每个位段则表示delegate对象是否实现了协议中的相关方法。用法如下:
#import "WXKSimulateFetcher.h"@interface WXKSimulateFetcher () { struct { unsigned int didStartLog :1; unsigned int didAlwaysLog :1; }_delegateFlags;}@property (nonatomic, strong) dispatch_source_t timer;@end@implementation WXKSimulateFetcher- (void)doLog { if (_delegateFlags.didStartLog) { [_delegate fetchStart:self alwaysLogString:@"startLog"]; } /* 使用定时器模拟频繁回调 */ __block int count = 0; dispatch_queue_t queue = dispatch_get_main_queue(); self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)); uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC); dispatch_source_set_timer(self.timer, start, interval, 0); dispatch_source_set_event_handler(self.timer, ^{ if ([_delegate respondsToSelector:@selector(fetchStart:alwaysLogString:)]) { [_delegate fetchStart:self alwaysLogString:@"alwaysLog"]; } count++; if (count == 10) { dispatch_cancel(self.timer); self.timer = nil; } }); dispatch_resume(self.timer);}- (void)setDelegate:(id)delegate { _delegate = delegate; _delegateFlags.didStartLog = [delegate respondsToSelector:@selector(fetchStart:didStartLogString:)]; _delegateFlags.didAlwaysLog = [delegate respondsToSelector:@selector(fetchStart:alwaysLogString:)];}@end
这样的话每次调用delegate的相关方法之前,就不用频繁检测了。