Objective-C中的複製忍者卡卡西---NSProxy

周老實發表於2017-12-29

是不是曾經在別人面前誇下海口:Objective-C中所有的類的基類都是NSObject;我之前也一直也這麼以為的,但是認真看了下官方文件:

Objective-C中的複製忍者卡卡西---NSProxy

啪啪,好響,好疼~(看來英文水平也很重要啊......) 不賣關子了,我們今天要討論的就是NSProxy。它是跟NSObject屬於同一級別的類,是個抽象類,只是實現了<NSObject>的協議;

Objective-C中的複製忍者卡卡西---NSProxy

按照官方的定義:NSProxy是一個為物件定義介面的抽象父類,並且為其它物件或者一些不存在的物件扮演了替身角色。通常,給proxy的訊息被轉發給實際物件或者導致proxy載入(轉化它為)實際物件。NSProxy的子類能被用來實現透明的分散式訊息(例如:NSDistantObject)或者延緩要花費昂貴代價建立的物件的實現。

下面我們看看NSProxy是怎麼複製別的類(幾乎所有類)的:

我們先建立一個類繼承自NSProxy:

#import "KakashiProxy.h"

@interface KakashiProxy ()

@property(nonatomic,strong)NSObject *objc;

@end

@implementation KakashiProxy

- (void)changeObj:(NSObject *)obj {
    
    self.objc = obj;
    
}

//方法簽名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    
    NSMethodSignature *signature = nil;
    if ([self.objc methodSignatureForSelector:sel]) {
        signature = [self.objc methodSignatureForSelector:sel];
    }else {
        signature = [super methodSignatureForSelector:sel];
    }
    
    return signature;
}

//呼叫方法實現
- (void)forwardInvocation:(NSInvocation *)invocation {
    
    if ([self.objc respondsToSelector:invocation.selector]) {
        NSString *selName = NSStringFromSelector(invocation.selector);
        NSLog(@"selector name : %@",selName);
        //這裡我們可以做一些邏輯處理,比如埋點統計之類的
        [invocation invokeWithTarget:self.objc];
    }else {
        [super forwardInvocation:invocation];
    }
    
}

@end
複製程式碼

可以看到我們重寫了兩個方法:methodSignatureForSelector:forwardInvocation:(NSInvocation *)invocation,前者是實現方法簽名的,我們可以在這個方法中,直接將我們複製的物件進行方法簽名,然後生成NSInvocation。接著方法forwardInvocation會被呼叫,在這個方法中,我們直接將invacationtarget設定為我們複製的物件。在這個方法中我們可以做一點事情,比如根據類名做一些邏輯,也可以做一些資料埋點之類的。 然後我們看看怎麼使用這個複製忍者的: 我們隨便定義兩個類,併為每個類設定了一個方法:

Parker-Dog.h

    #import "Parker-Dog.h"
    
    @implementation Parker_Dog
    
    - (void)bite {
        NSLog(@"卡卡西召喚通靈獸Paker並咬住了再不斬!");
    }
    
    @end
複製程式碼

Wood.h

    #import "Wood.h"

    @implementation Wood
    
    - (void)boom{
    
        NSLog(@"變!木頭!...");
        
    }
複製程式碼

呼叫:

    //初始化一個木頭
    Wood *wood = [[Wood alloc] init];
    //初始化一個帕克
    Parker_Dog  *paker = [[Parker_Dog alloc] init];
    //初始化一個卡卡西
    KakashiProxy *proxy = [KakashiProxy alloc];
    
    //變木頭
    [proxy changeObj:wood];
    //呼叫木頭的方法
    [proxy performSelector:@selector(boom) withObject:nil];
    
    //變帕克
    [proxy changeObj:paker];
    //呼叫帕克的方法
    [proxy performSelector:@selector(bite) withObject:nil];
複製程式碼

控制檯輸出:

Objective-C中的複製忍者卡卡西---NSProxy

這樣,我們通過proxy這個類實現了複製woodpaker,可以分別呼叫各個類的方法了。

總結

其實也不能叫複製吧,大家也可以看到,這個實現其實就是利用了runtime的訊息轉發機制。這篇文章就算是對NSProxy的一個簡單認識吧,還有些NSProxy的高階用法可能筆者還沒有學習到,還要繼續努力,有時間需要深入研究下這個特殊的類。

參看文章

相關文章