iOS | NSProxy

簡易安丶發表於2018-04-25

Objective-C作為一種動態訊息型語言,其機制不同於Java ,C#等編譯型語言.
它將資料型別的確定等工作推遲到了執行時期來執行,並且它呼叫方法的方式實質是像物件傳送訊息,根據selector在物件的本類以及父類中的方法列表進行查詢,如果都找不到就會啟動訊息轉發機制.

回到正題,這個話題我想談下OC的單繼承原則.
OC確實是只能單繼承的語言,但是基於執行時的機制,卻有一種方法讓它來實現一下"偽多繼承".就是利用NSProxy這個類.

NSProxy是和NSObject同級的一個類,可以說它是一個虛擬類,它只是實現了<NSObject>的協議.它的作用有點類似於一個複製類,有人曾經笑談它是卡卡西的複製忍術,想想其實也挺貼切的,其實原理確實如此.

過程:
用一個繼承於NSProxy的子類,在它內部實現一些方法,暴露一個公開方法transform,這個方法是使它變身的關鍵.然後它變身之後可以對那些物件傳送訊息,並且可以在內部攔截訊息的內容並修改.

可以說,幾乎可以變身成為任何物件.

直接上個程式碼來展示下

JanProxy.h

#import <Foundation/Foundation.h>

@interface JanProxy : NSProxy

- (void)transformObjc:(NSObject *)objc;

@end

JanProxy.m

#import "JanProxy.h"

@interface JanProxy ()

@property(nonatomic,strong)NSObject *objc;

@end

@implementation JanProxy



- (void)transformObjc:(NSObject *)objc
{
    //複製物件
    self.objc = objc;
}

//2.有了方法簽名之後就會呼叫方法實現
- (void)forwardInvocation:(NSInvocation *)invocation
{
    if (self.objc) {
        //攔截方法的執行者為複製的物件
        [invocation setTarget:self.objc];
           
        if ([self.objc isKindOfClass:[NSClassFromString(@"Teacher") class]]) {
            //攔截引數 Argument:表示的是方法的引數  index:表示的是方法引數的下標
            NSString *str = @"攔截訊息";
            [invocation setArgument:&str atIndex:2];
        }
        
        //開始呼叫方法
        [invocation invoke];
    }
    
}

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

@end

使用方法


     Dog *dog = [[Dog alloc]init];
    
    //OC中方法的呼叫本質上是給這個物件傳送一個訊息
    Cat *cat = [[Cat alloc] init];
 
    //開始複製攔截方法
    JanProxy *proxy = [JanProxy alloc];
    //開始變身成貓
    [proxy transformObjc:cat];
    //開始調貓的方法
    [proxy performSelector:@selector(eat:) withObject:@"貓發出訊息"];
    
    //開始變身成狗
    [proxy transformObjc:Dog];
    //開始呼叫學生的方法
    [proxy performSelector:@selector(shut)];

最後的結果

控制檯輸出結果

發現沒有,貓發出訊息已經被子類的內部攔截並且做出了修改.

總結

OC中存在這麼一個默默無聞的類NSProxy,填補了"多繼承"這個空白區.

相關文章