iOS Class 使用NSProxy和NSObject設計代理類的差異,nsproxynsobject
經常發現在一些需要使用訊息轉寄而建立代理類時, 不同的程式員都有著不同的使用方法, 有些採用繼承於NSObject, 而有一些採用繼承自NSProxy. 二者都是Foundation架構中的基類, 並且都實現了<NSObject>
這個介面, 從命名和文檔中看NSProxy天生就是用來幹這個事情的. 但即便如此, 它們卻都定義了相同的訊息轉寄的介面, 那我們在使用二者來完成這個工作時有什麼差異呢.
先貼一下通過二者來建立代理類的最基本實現代碼.
繼承自NSProxy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
@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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
@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.經測試發現以下兩個在<NSObject>
中定義的介面, 在二者之間表現是不一致的:
1 2 3 4 5 6 7 8 9
|
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]]);
|
結果會輸出完成不同的結論:
也就是說通過繼承自NSObject的代理類是不會自動轉寄respondsToSelector:和isKindOfClass:這兩個方法的, 而繼承自NSProxy的代理類卻是可以的. 測試<NSObject>
中定義的其它介面二者表現都是一致的.
2.NSObject的所有Category中定義的方法無法在THProxyB中完成轉寄
舉一個很常見的例子, valueForKey:是定義在NSKeyValueCoding這個NSObject的Category中的方法, 嘗試二者執行的表現.
1 2
|
NSLog(@"%@",[proxyA valueForKey:@"length"]); NSLog(@"%@",[proxyB valueForKey:@"length"]);
|
這段代碼第一句能正確運行, 但第二行卻會拋出異常, 分析最終原因其實很簡單, 因為valueForKey:是NSObject的Category中定義的方法, 讓NSObject具備了這樣的介面, 而訊息轉寄是只有當接收者無法處理時才會通過forwardInvocation:來尋求能夠處理的對象.
3.結論: 如此看來NSProxy確實更適合實現做為訊息轉寄的代理類, 因為作為一個抽象類別, NSProxy自身能夠處理的方法極小(僅<NSObject>
介面中定義的部分方法), 所以其它方法都能夠按照設計的預期被轉寄到被代理的對象中.