Objective-C delegate 性能小优化
之前也是对 delegate
怨念颇深——每次都需要检查 deleagte
并且检查是否响应了某个方法。不过那个时候的怨念是来自于 需要写那么一大段代码,而不是怨念性能。
过去的解决方法
当时的解决办法是写了这么一个 宏 :
#define MBCCheckDelegate(methodName) (self.delegate && [self.delegate respondsToSelector:@selector(methodName)])
实际上写成 内联函数 也是可以的。不过到了这一步又开始有点犯懒了,所以一直没有继续。
优化
直到今天看到了 参考1 的这篇文章。文章中提到了可以缓存 respondsToSelector:
方法的结果,让我感慨原来还能这么做!
@protocol SomethingDelegate <NSObject>
@optional
- (void)something:(id)something didFinishLoadingItem:(id)item;
- (void)something:(id)something didFailWithError:(NSError *)error;
@end
@interface Something : NSObject
@property (nonatomic, weak) id <SomethingDelegate> delegate;
@end
@implementation Something {
struct {
unsigned int didFinishLoadingItem:1;
unsigned int didFailWithError:1;
} delegateRespondsTo;
}
@synthesize delegate;
- (void)setDelegate:(id <JSSomethingDelegate>)aDelegate {
if (delegate != aDelegate) {
delegate = aDelegate;
delegateRespondsTo.didFinishLoadingItem = [delegate respondsToSelector:@selector(something:didFinishLoadingItem:)];
delegateRespondsTo.didFailWithError = [delegate respondsToSelector:@selector(something:didFailWithError:)];
}
}
@end
如上所示,便是使用 结构体 来缓存结果。我们也可以使用其他的数据结构来达到我们的目的。
然后在实际通过 delegate 对象调用方法时,只需要判断对应的属性即可。
但是有一点需要留意,Objective-C 可以动态添加方法,所以设置时的情况未必能代表调用时的情况。具体要不要用这种方法还要考虑团队内的开发氛围。
简写的 delegate
在有 protocol
之前,一般是给 NSObject
加一个 category
来声明 delegate
可以实现的方法。比如,现如今 CALayer
还有:
@interface NSObject(CALayerDelegate)
- (void)displayLayer:(CALayer *)layer;
// ... other methods here
@end
意思是告诉编译器,任何对象都可以实现 displayLayer:
方法。
如果这样写的话,在调用方法之前也要同样要用上面提到的 -respondsToSelector:
来检查。只要让 delegate
对象实现这个方法,然后把它保存在 delegate
属性里就行了,不用写 protocol
什么的。
苹果官方的库里有不少这样的东西,但是新写的代码最好还是用上面声明 protocol
的正规写法。
因为这种简写方法会污染NSObject(会干扰代码自动补全),并且也让编译器不易检查出打错字之类的 error。