經常發現在一些需要使用消息轉發而創建代理類時, 不同的程序員都有著不同的使用方法, 有些采用繼承於NSObject, 而有一些采用繼承自NSProxy. 二者都是Foundation框架中的基類, 並且都實現了
先貼一下通過二者來創建代理類的最基本實現代碼.
繼承自NSProxy
@interface THProxyA : NSProxy @property (nonatomic, strong) id target; @end @implementation THProxyA - (id)initWithObject:(id)object { self.target = object; return self; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { return [self.target methodSignatureForSelector:selector]; } - (void)forwardInvocation:(NSInvocation *)invocation { [invocation invokeWithTarget:self.target]; } @end
繼承自NSObject
@interface THProxyB : NSObject @property (nonatomic, strong) id target; @end @implementation THProxyB - (id)initWithObject:(id)object { self = [super init]; if (self) { self.target = object; } return self; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { return [self.target methodSignatureForSelector:selector]; } - (void)forwardInvocation:(NSInvocation *)invocation { [invocation invokeWithTarget:self.target]; } @end
代碼基本是一致的, 除了初始化時規范的寫法有細節差異, 這個差異是因為NSProxy這個基類沒有定義默認的init方法.
1.經測試發現以下兩個在
NSString *string = @"test"; THProxyA *proxyA = [[THProxyA alloc] initWithObject:string]; THProxyB *proxyB = [[THProxyB alloc] initWithObject:string]; NSLog(@"%d", [proxyA respondsToSelector:@selector(length)]); NSLog(@"%d", [proxyB respondsToSelector:@selector(length)]); NSLog(@"%d", [proxyA isKindOfClass:[NSString class]]); NSLog(@"%d", [proxyB isKindOfClass:[NSString class]]);
結果會輸出完成不同的結論:
1 0 1 0
也就是說通過繼承自NSObject的代理類是不會自動轉發respondsToSelector:和isKindOfClass:這兩個方法的, 而繼承自NSProxy的代理類卻是可以的. 測試
2.NSObject的所有Category中定義的方法無法在THProxyB中完成轉發
舉一個很常見的例子, valueForKey:是定義在NSKeyValueCoding這個NSObject的Category中的方法, 嘗試二者執行的表現.
NSLog(@"%@",[proxyA valueForKey:@"length"]); NSLog(@"%@",[proxyB valueForKey:@"length"]);
這段代碼第一句能正確運行, 但第二行卻會拋出異常, 分析最終原因其實很簡單, 因為valueForKey:是NSObject的Category中定義的方法, 讓NSObject具備了這樣的接口, 而消息轉發是只有當接收者無法處理時才會通過forwardInvocation:來尋求能夠處理的對象.
3.結論: 如此看來NSProxy確實更適合實現做為消息轉發的代理類, 因為作為一個抽象類, NSProxy自身能夠處理的方法極小(僅