NSObject類是Objective-C中大部分類的基類。
但不是很多人知道除了NSObject之外的另一個(gè)基類——NSProxy.
總的來說寞埠,NSProxy是一個(gè)虛類崖技,你可以通過繼承它稿静,并重寫這兩個(gè)方法以實(shí)現(xiàn)消息轉(zhuǎn)發(fā)到另一個(gè)實(shí)例:
- (void)forwardInvocation:(NSInvocation *)invocation;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel ;
現(xiàn)在NSProxy的真面目終于浮出水面:負(fù)責(zé)將消息轉(zhuǎn)發(fā)到真正的target的代理類娇掏。舉個(gè)例子,你想要賣一件二手物品空执,但是你并不想直接跟賣家接觸(直接向target發(fā)消息)浪箭,這時(shí)你去找了一個(gè)第三方,你告訴這個(gè)第三方你要買什么辨绊、出多少錢買奶栖、什么時(shí)候要等(向代理發(fā)消息),第三方再去跟賣家接觸并把這些信息轉(zhuǎn)告賣家(轉(zhuǎn)發(fā)消息給真實(shí)的target),最后通過第三方去完成這個(gè)交易宣鄙。
了解完NSProxy是是什么以后袍镀,那么它究竟能幫我們干些什么呢?
通過NSProxy在Objective-C中模擬多繼承
多繼承在編程中可以說是比較有用的特性冻晤。舉個(gè)例子苇羡,原本有兩個(gè)相互獨(dú)立的類A和類B,它們各自繼承各自的父類鼻弧,項(xiàng)目進(jìn)行地好好的设江,突然有一天產(chǎn)品經(jīng)理過來告訴你,我要在下個(gè)版本加一個(gè)xxxxx的特性攘轩,非常緊急叉存。一臉懵逼的你發(fā)現(xiàn)如果要實(shí)現(xiàn)這個(gè)特性,你需要對(duì)類A以及其父類作很大的修改度帮,代價(jià)非常之高歼捏。突然你意識(shí)到原來類B的父類已經(jīng)有類似的功能,你只需要讓類A繼承于類B的父類并重寫其某些方法就能實(shí)現(xiàn)够傍,這樣做高效且低風(fēng)險(xiǎn)甫菠,于是你屁顛屁顛地?cái)]起了代碼挠铲。
可是冕屯,Objective-C卻不支持這樣一個(gè)強(qiáng)大的特性。不過NSProxy可以幫我們?cè)谀撤N程度上(這只是一個(gè)模擬的多繼承拂苹,并不是完全的多繼承)解決這個(gè)問題:
現(xiàn)在假設(shè)我們想要去買書安聘,但是我懶癌犯了,不想直接去書店(供應(yīng)商)買瓢棒,如果有一個(gè)跑腿的人(經(jīng)銷商)幫我去書店買完浴韭,我再跟他買。同時(shí)脯宿,我買完書又想買件衣服念颈,我又可以很輕松地在他那里買到一件衣服(多繼承)。
首先连霉,我們定義BookProvider類與ClothesProvider類作為基類榴芳。
TDBookProvider###
// TDBookProvider.h
#import
@protocol TDBookProviderProtocol
- (void)purchaseBookWithTitle:(NSString *)bookTitle;
@end
@interface TDBookProvider : NSObject
@end
// TDBookProvider.m
#import "TDBookProvider.h"
@interface TDBookProvider ()
@end
@implementation TDBookProvider
- (void)purchaseBookWithTitle:(NSString *)bookTitle{
NSLog(@"You've bought \"%@\"",bookTitle);
}
@end
TDClothesProvider.h
// TDClothesProvider.h
#import
typedef NS_ENUM (NSInteger, TDClothesSize){
TDClothesSizeSmall = 0,
TDClothesSizeMedium,
TDClothesSizeLarge
};
@protocol TDClothesProviderProtocol
- (void)purchaseClothesWithSize:(TDClothesSize )size;
@end
@interface TDClothesProvider : NSObject
@end
// TDClothesProvider.m
#import "TDClothesProvider.h"
@interface TDClothesProvider ()
@end
@implementation TDClothesProvider
- (void)purchaseClothesWithSize:(TDClothesSize )size{
NSString *sizeStr;
switch (size) {
case TDClothesSizeLarge:
sizeStr = @"large size";
break;
case TDClothesSizeMedium:
sizeStr = @"medium size";
break;
case TDClothesSizeSmall:
sizeStr = @"small size";
break;
default:
break;
}
NSLog(@"You've bought some clothes of %@",sizeStr);
}
@end
// TDDealerProxy.h
#import
#import "TDBookProvider.h"
#import "TDClothesProvider.h"
@interface TDDealerProxy : NSProxy
+ (instancetype )dealerProxy;
@end
// TDDealerProxy.m
#import "TDDealerProxy.h"
#import <objc/runtime.h>
@interface TDDealerProxy () {
TDBookProvider *_bookProvider;
TDClothesProvider *_clothesProvider;
NSMutableDictionary *_methodsMap;
}
@end
@implementation TDDealerProxy
#pragma mark - class method
+ (instancetype)dealerProxy{
return [[TDDealerProxy alloc] init];
}
#pragma mark - init
- (instancetype)init{
_methodsMap = [NSMutableDictionary dictionary];
_bookProvider = [[TDBookProvider alloc] init];
_clothesProvider = [[TDClothesProvider alloc] init];
//映射target及其對(duì)應(yīng)方法名
[self _registerMethodsWithTarget:_bookProvider];
[self _registerMethodsWithTarget:_clothesProvider];
return self;
}
#pragma mark - private method
- (void)_registerMethodsWithTarget:(id )target{
unsigned int numberOfMethods = 0;
//獲取target方法列表
Method *method_list = class_copyMethodList([target class], &numberOfMethods);
for (int i = 0; i < numberOfMethods; i ++) {
//獲取方法名并存入字典
Method temp_method = method_list[i];
SEL temp_sel = method_getName(temp_method);
const char *temp_method_name = sel_getName(temp_sel);
[_methodsMap setObject:target forKey:[NSString stringWithUTF8String:temp_method_name]];
}
free(method_list);
}
#pragma mark - NSProxy override methods
- (void)forwardInvocation:(NSInvocation *)invocation{
//獲取當(dāng)前選擇子
SEL sel = invocation.selector;
//獲取選擇子方法名
NSString *methodName = NSStringFromSelector(sel);
//在字典中查找對(duì)應(yīng)的target
id target = _methodsMap[methodName];
//檢查target
if (target && [target respondsToSelector:sel]) {
[invocation invokeWithTarget:target];
} else {
[super forwardInvocation:invocation];
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
//獲取選擇子方法名
NSString *methodName = NSStringFromSelector(sel);
//在字典中查找對(duì)應(yīng)的target
id target = _methodsMap[methodName];
//檢查target
if (target && [target respondsToSelector:sel]) {
return [target methodSignatureForSelector:sel];
} else {
return [super methodSignatureForSelector:sel];
}
}
@end
這里有兩個(gè)要注意的問題:1、TDDealerProxy這個(gè)子類必須要遵循之前定義的兩個(gè)協(xié)議TDBookProviderProtocol與TDClothesProviderProtocol跺撼,目的是騙過編譯器窟感,讓編譯器認(rèn)為這個(gè)類實(shí)現(xiàn)了上面兩個(gè)協(xié)議2、NSProxy類是沒有init方法的歉井,也就是說如果我們要獲得一個(gè)NSProxy的實(shí)例柿祈,代碼只需要這樣:
NSProxy *proxyInstance = [NSProxy alloc];
在AppDelagate 中 實(shí)現(xiàn):###
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
TDDealerProxy *dealerProxy = [TDDealerProxy dealerProxy];
[dealerProxy purchaseBookWithTitle:@"Swift 100 Tips"];
[dealerProxy purchaseClothesWithSize:TDClothesSizeMedium];
// Override point for customization after application launch.
return YES;
}