我們在編寫代理協議時,代理協議中的方法一般都是@optional的,代理方不一定實現了代理協議中的所有方法。那麼我們在代理方上調用可選方法時候,最好提前使用類型信息查詢方法來判斷這個委託對象是否能夠相應相關協議方法:
if (delegate respondsToSelector:@selector(selector)) {
[delegate selector];
}
用 respondsToSelector: 來判斷代理方是否實現了相關的方法,如果實現了就調用,如果沒有實現就不執行任何操作。
在實現代理模式時,如果協議中的方法是可選的,就會寫出一大批類似上面這樣的代碼來。即使很容易就能用代碼查出某個代理方能否相應特定的方法,可是如果頻繁執行此操作的話,那麼除了第一次檢測的結果有用之外,後續的檢測可能都是多餘的。如果代理方本身沒變,那麼不大可能會突然響應某個原來不能響應的方法,也不太會突然無法響應某個原來可以響應的方法。因此,我們可以吧代理方能否響應某個協議方法這一信息緩存起來,以優化程序效率。
將方法響應能力緩存起來的最佳途徑是使用"位域"(又稱位段):
struct data {
unsigned int fieldA : 8;
unsigned int fieldB : 4;
unsigned int fieldC : 2;
unsigned int fieldD : 1;
}
這data這個結構體中,field位段佔用8個二進制位,fieldB佔用4個,fieldC佔用2個,fieldD佔用1個。於是,fieldA可以表示0~255之間的值,fieldD可以表示0或1這兩個值。如果創建一個只有大小爲1的位段的結構體,就可以把布爾值塞進一小塊數據裏了。
// .h文件
#import <Foundation/Foundation.h>
@protocol testDelegate <NSObject>
@optional
-(void)delegateFuncA:(NSString*)argument;
-(void)delegateFuncB:(NSString*)argument;
-(void)delegateFuncC:(NSString*)argument;
@end
@interface testClass : NSObject
@property(nonatomic, weak) id<testDelegate> delegate;
@end
// .m文件
#import "testClass.h"
@interface testClass(){
struct {
unsigned int delegateFuncA : 1;
unsigned int delegateFuncB : 1;
unsigned int delegateFuncC : 1;
} _delegateFlags;
}
@end
@implementation testClass
-(void)setDelegate:(id<testDelegate>)delegate{
_delegate = delegate;
_delegateFlags.delegateFuncA = [delegate respondsToSelector:@selector(delegateFuncA:)];
_delegateFlags.delegateFuncB = [delegate respondsToSelector:@selector(delegateFuncB:)];
_delegateFlags.delegateFuncC = [delegate respondsToSelector:@selector(delegateFuncC:)];
}
@end
在delegate的setter方法中這樣緩存起來響應方法的情況。這樣的話每次調用delegate的相關方法之前,就不用檢測代理方是否能夠響應給定的方法了,直接查詢結構體中的標誌即可:
if (_delegateFlags.delegateFuncA) {
[_delegate delegateFuncA:@""];
}
在相關方法調用很多次時,值得進行這種優化。