Objective-C作為一種動態(tài)消息型語言,其機制不同于Java ,C#等編譯型語言.
它將數(shù)據(jù)類型的確定等工作推遲到了運行時期來執(zhí)行,并且它調(diào)用方法的方式實質(zhì)是像對象發(fā)送消息,根據(jù)selector在對象的本類以及父類中的方法列表進行查找,如果都找不到就會啟動消息轉(zhuǎn)發(fā)機制.
回到正題,這個話題我想談下OC的單繼承原則.
OC確實是只能單繼承的語言,但是基于運行時的機制,卻有一種方法讓它來實現(xiàn)一下"偽多繼承".就是利用NSProxy這個類.
NSProxy是和NSObject同級的一個類,可以說它是一個虛擬類,它只是實現(xiàn)了<NSObject>的協(xié)議.它的作用有點類似于一個復制類,有人曾經(jīng)笑談它是卡卡西的復制忍術,想想其實也挺貼切的,其實原理確實如此.
過程:
用一個繼承于NSProxy的子類,在它內(nèi)部實現(xiàn)一些方法,暴露一個公開方法transform,這個方法是使它變身的關鍵.然后它變身之后可以對那些對象發(fā)送消息,并且可以在內(nèi)部攔截消息的內(nèi)容并修改.
可以說,幾乎可以變身成為任何對象.
直接上個代碼來展示下
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.有了方法簽名之后就會調(diào)用方法實現(xiàn)
- (void)forwardInvocation:(NSInvocation *)invocation
{
if (self.objc) {
//攔截方法的執(zhí)行者為復制的對象
[invocation setTarget:self.objc];
if ([self.objc isKindOfClass:[NSClassFromString(@"Cat") class]]) {
//攔截參數(shù) Argument:表示的是方法的參數(shù) index:表示的是方法參數(shù)的下標
NSString *str = @"攔截消息";
[invocation setArgument:&str atIndex:2];
}
//開始調(diào)用方法
[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中方法的調(diào)用本質(zhì)上是給這個對象發(fā)送一個消息
Cat *cat = [[Cat alloc] init];
//開始復制攔截方法
JanProxy *proxy = [JanProxy alloc];
//開始變身成貓
[proxy transformObjc:cat];
//開始調(diào)貓的方法
[proxy performSelector:@selector(eat:) withObject:@"貓發(fā)出消息"];
//開始變身成狗
[proxy transformObjc:Dog];
//開始調(diào)用學生的方法
[proxy performSelector:@selector(shut)];
最后的結果
發(fā)現(xiàn)沒有,貓發(fā)出消息已經(jīng)被子類的內(nèi)部攔截并且做出了修改.
總結
OC中存在這么一個默默無聞的類NSProxy,填補了"多繼承"這個空白區(qū).