我一直以為OC中所有的類都繼承與NSObject,But...

在OC中少見的不繼承于NSObject 的類------>NSProxy,下面來介紹一下NSProxy
NSObject類是Objective-C中大部分類的基類。但不是很多人知道除了NSObject之外的另一個基類——NSProxy.
我們可以這樣認為,NSProxy是一個虛類,它可以被繼承,并重寫下面兩個方法來實現消息轉發到另一個實例

- (void)forwardInvocation:(NSInvocation *)invocation;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel ;

NSProxy是什么呢?其實它是什么玩意呢?大家可以去查閱一下Apple的官方API,其實NSProxy就是:負責將消息轉發到真正的target的代理類。
什么說的是什么意思呢?下面來分析一下:在我們生活中如下自己想買某件商品,但是自己又不愿意親自去買(向代理發消息),這樣我們可以去找個跑路的人告訴他自己要干啥事幫著自己買(轉發消息給真實的target),然后間接去完成某件事

在我們了解完什么是NSProxy之后,然后繼續深入

通過NSProxy在Objective-C中模擬多繼承###

多繼承在編程中可以說是比較有用的特性。舉個例子,原本有兩個相互獨立的類A和類B,它們各自繼承各自的父類,項目進行地好好的,突然有一天產品經理過來告訴你,我要在下個版本加一個xxxxx的特性,非常緊急。一臉懵逼的你發現如果要實現這個特性,你需要對類A以及其父類作很大的修改,代價非常之高。突然你意識到原來類B的父類已經有類似的功能,你只需要讓類A繼承于類B的父類并重寫其某些方法就能實現,這樣做高效且低風險,于是你屁顛屁顛地擼起了代碼。

可是,Objective-C卻不支持這樣一個強大的特性。不過NSProxy可以幫我們在某種程度上(這只是一個模擬的多繼承,并不是完全的多繼承)解決這個問題:

現在假設我們想要去買書,但是我懶癌犯了,不想直接去書店(供應商)買,如果有一個跑腿的人(經銷商)幫我去書店買完,我再跟他買。同時,我買完書又想買件衣服,我又可以很輕松地在他那里買到一件衣服(多繼承)。

首先,我們定義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

這里要注意:一定要通過protocol來聲明接口,而不是直接在類的@interfere中定義。因為通過protocol來聲明接口,然后讓proxy類遵循此協議,可以騙過編譯器防止編譯器提示proxy類未聲明接口的錯誤。這個問題下面可以看到。
**
現在兩個Provider的類寫完,我們可以直接向供應商買東西了,但這跟我們的需求還有很大差異,我們需要一個中間的經銷商
**

//  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及其對應方法名
    [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{
    //獲取當前選擇子
    SEL sel = invocation.selector;

    //獲取選擇子方法名
    NSString *methodName = NSStringFromSelector(sel);

    //在字典中查找對應的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);

    //在字典中查找對應的target
    id target = _methodsMap[methodName];

    //檢查target
    if (target && [target respondsToSelector:sel]) {
        return [target methodSignatureForSelector:sel];
    } else {
        return [super methodSignatureForSelector:sel];
    }
}

@end

這里有兩個要注意的問題:1、TDDealerProxy這個子類必須要遵循之前定義的兩個協議TDBookProviderProtocol與TDClothesProviderProtocol,目的是騙過編譯器,讓編譯器認為這個類實現了上面兩個協議2、NSProxy類是沒有init方法的,也就是說如果我們要獲得一個NSProxy的實例,代碼只需要這樣:

NSProxy *proxyInstance = [NSProxy alloc];

但是我們需要在AppDelagate 中 實現一下方法:

- (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;
}

///摘自http://www.lxweimin.com/p/8d42cc3a6296

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容