Objective-C runtime之消息轉發機制(三)

Objective-C runtime之消息轉發機制(三)

作者:wangzz
轉載請註明出處
如果覺得文章對你有所幫助,請通過留言或關注微信公衆帳號wangzzstrive來支持我,謝謝!

學了那麼久的Objective-C,給我的感覺就是它什麼都是動態的,你將會聽到一個新的名詞:

一、動態方法解析

1、+(BOOL) resolveInstanceMethod:(SEL) sel

這是NSObject根類提供的類方法,調用時機爲當被調用的方法實現部分沒有找到,而消息轉發機制啓動之前的這個中間時刻。

2、@dynamic關鍵字

Objective-C2.0 提供了@dynamic關鍵字。這個關鍵字有兩個作用:

①告訴編譯器不要創建實現屬性所用的實例變量;

②告訴編譯器不要創建該屬性的get和setter方法。

如果我們在@interface接口文件中聲明瞭一個屬性,如下所示:

@property(nonatomic,retain) NSString    *name;

默認情況下,編譯器會爲當前類自動生成一個NSString   *_name的實例變量(如果想改變實例變量的名稱可以用@synthesize關鍵字),同時會生成兩個名爲- (NSString *)name和- (void)setName:(NSString *)aName的存取方法。

@dynamic關鍵字就是告訴編譯器不要做這些事,同時在使用了存取方法時也不要報錯,即讓編譯器相信存取方法會在運行時找到。

比如在@implementation文件中做了如下聲明:

@dynamic name;
如果使用了name屬性的setter方法,又不想在運行時崩潰,就可以在運行時做點動作:

void dynamicMethodIMP(id self, SEL _cmd)
{
    // implementation ....
}

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    NSLog(@"sel is %@", NSStringFromSelector(sel));
    if(sel == @selector(setName:)){
        class_addMethod([self class],sel,(IMP)dynamicMethodIMP,"v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

在resolveInstanceMethod的實現中,我們通過class_addMethod方法動態的向當前對象增加了dynamicMethodIMP函數,來代替-(void)setName:(NSString *)name的實現部分,從而達到了動態生成name屬性方法的目的。

值得說明的是:

在上個例子中,我們自己實現了-(void)setName:(NSString *)name方法,則在運行的時候,調用完我們實現的-(void)setName:(NSString *)name方法後,運行時系統仍然會調+(BOOL) resolveInstanceMethod:(SEL) sel方法,只不過這裏的sel會變成_doZombieMe,從而我們實現重定向的if分支就進不去了,即我們實現的方法不會被覆蓋。

②"v@:"屬於Objective-C類型編碼的內容,感興趣的同學可以自己google一下。

二、runtime system消息轉發機制

對象是謙恭的,它會接收所有發送過來的消息,哪怕這些消息自己無法響應。問題來了:當對象無法響應這些消息時怎麼辦?runtime提供了消息轉發機制來處理該問題。

當外部調用的某個方法對象沒有實現,而且resolveInstanceMethod方法中也沒有做重定向處理時,就會觸發- (void)forwardInvocation:(NSInvocation *)anInvocation方法。在該方法中,可以實現對不能處理的消息做的一些默認處理,也可以以其它的某種方式來避免錯誤被拋出。像forwardInvocation:的名字一樣,這個方法通常用來將不能處理的消息轉發給其它的對象。通常我們重寫該方法的方式如下所示:

-(void)forwardInvocation:(NSInvocation *)invocation
{
	SEL invSEL = invocation.selector;
	if ([someOtherObject respondsToSelector:invSEL])
		[anInvocation invokeWithTarget:someOtherObject];
	} else {
		[self doesNotRecognizeSelector:invSEL]; 
	}                                                                          
}

怎麼看着有點像多繼承呀???你說對了,消息轉發提供了多重繼承的很多特性。然而,兩者有很大的不同:多重繼承是將不同的行爲封裝到單個的對象中,有可能導致龐大的,複雜的對象。而消息轉發是將問題分解到更小的對象中,但是又以一種對消息發送對象來說完全透明的方式將這些對象聯繫起來。總之,Objective-C通過這種方式,一定程度上減小了自己不支持多繼承的劣勢。


經過半個月的時間,自己總結、整理出了這三篇文章,到這裏,對Objective-C運行時的學習算是告一段落了。文筆的原因,文章結構不是很清晰,還請見諒。對運行時理解不到位,或者是有錯誤的地方,還請廣大博友指出,感激不盡!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章