runtime 基礎(chǔ)篇(三)

每次都想自己寫, 但是時間太少, 最重要的是每次想寫, 都發(fā)現(xiàn)很多寫的太好的文章, 找一個寫的深入淺出的文章轉(zhuǎn)載一下
轉(zhuǎn)載地址: http://www.cnblogs.com/biosli/p/NSObject_inherit_2.html

消息動態(tài)解析

  • Objective-C是一門動態(tài)語言,一個函數(shù)是由一個selector(SEL),和一個implement(IML)組成的。
  • 系統(tǒng)根據(jù)@selector(XXX)查找方法, 如果找不到, 系統(tǒng)會給程序幾次機(jī)會來仍然使程序正常運(yùn)行, 如果這幾次機(jī)會都浪費(fèi)了, 才會拋出異常

下圖是objc_msgSend調(diào)用時,查找SEL的IML的過程:


運(yùn)行時查找函數(shù)的流程.png

1 .resolveInstanceMethod函數(shù)

+ (BOOL)resolveInstanceMethod:(SEL)name

這個函數(shù)在運(yùn)行時(runtime),沒有找到SEL的IML時就會執(zhí)行。這個函數(shù)是給類利用class_addMethod添加函數(shù)的機(jī)會。
根據(jù)文檔,如果實(shí)現(xiàn)了添加函數(shù)代碼則返回YES,未實(shí)現(xiàn)返回NO。
實(shí)現(xiàn)的例子:

//全局函數(shù)
void dynamicMethodIMP(id self, SEL _cmd)
{
    // implementation ....
}

@implementation MyTestObject
//…
//類函數(shù)
+ (BOOL) resolveInstanceMethod:(SEL)aSEL
{
    if (aSEL == @selector(resolveThisMethodDynamically))
    {
          class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
          return YES;
    }
    return [super resolveInstanceMethod:aSel];
}
//…
@end

注意事項(xiàng):
根據(jù)Demo實(shí)驗(yàn),這個函數(shù)返回的BOOL值系統(tǒng)實(shí)現(xiàn)的objc_msgSend函數(shù)并沒有參考,無論返回什么系統(tǒng)都會嘗試再次用SEL找IML,如果找到函數(shù)實(shí)現(xiàn)則執(zhí)行函數(shù)。如果找不到繼續(xù)其他查找流程。

2 .forwardingTargetForSelector:

- (id)forwardingTargetForSelector:(SEL)aSelector

流程到了這里,系統(tǒng)給了個將這個SEL轉(zhuǎn)給其他對象的機(jī)會。
返回參數(shù)是一個對象,如果這個對象非nil、非self的話,系統(tǒng)會將運(yùn)行的消息轉(zhuǎn)發(fā)給這個對象執(zhí)行。否則,繼續(xù)查找其他流程。
實(shí)現(xiàn)示例:

//轉(zhuǎn)發(fā)目標(biāo)類
@interface NoneClass : NSObject
@end

@implementation NoneClass
+(void)load
{
    NSLog(@"NoneClass _cmd: %@", NSStringFromSelector(_cmd));
}

- (void) noneClassMethod
{
    NSLog(@"_cmd: %@", NSStringFromSelector(_cmd));
}
@end

@implementation MyTestObject
//…
//將消息轉(zhuǎn)出某對象
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    NSLog(@"MyTestObject _cmd: %@", NSStringFromSelector(_cmd));

    NoneClass *none = [[NoneClass alloc] init];
    if ([none respondsToSelector: aSelector]) {
        return none;
    }
    
    return [super forwardingTargetForSelector: aSelector];
}
//…
@end

當(dāng)執(zhí)行MyTestObject對象執(zhí)行[myTestObject nonClassMethod]函數(shù)時,消息會拋到NoneClass對象中執(zhí)行。

3 . methodSignatureForSelector:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

這個函數(shù)和后面的forwardInvocation:是最后一個尋找IML的機(jī)會。這個函數(shù)讓重載方有機(jī)會拋出一個函數(shù)的簽名,再由后面的forwardInvocation:去執(zhí)行。

4 . forwardInvocation:

- (void)forwardInvocation:(NSInvocation *)anInvocation

真正執(zhí)行從methodSignatureForSelector:返回的NSMethodSignature。在這個函數(shù)里可以將NSInvocation多次轉(zhuǎn)發(fā)到多個對象中,這也是這種方式靈活的地方。(forwardingTargetForSelector只能以Selector的形式轉(zhuǎn)向一個對象)
下面這個示例代碼,詮釋了這種實(shí)現(xiàn)優(yōu)勢:

#import <Foundation/Foundation.h>
 
@interface Book : NSObject
{
    NSMutableDictionary *data;
}
//聲明了兩個setter/getter
@property (retain) NSString *title; 
@property (retain) NSString *author;
@end
 
@implementation Book
@dynamic title, author; //不自動生成實(shí)現(xiàn)
 
- (id)init
{
    if ((self = [super init])) {
        data = [[NSMutableDictionary alloc] init];
        [data setObject:@"Tom Sawyer" forKey:@"title"];
        [data setObject:@"Mark Twain" forKey:@"author"];
    }
    return self;
}
 
- (void)dealloc
{
    [data release];
    [super dealloc];
}
 
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
    NSString *sel = NSStringFromSelector(selector);
    if ([sel rangeOfString:@"set"].location == 0) {
        //動態(tài)造一個 setter函數(shù)
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    } else {
        //動態(tài)造一個 getter函數(shù)
        return [NSMethodSignature signatureWithObjCTypes:"@@:"];
    }
}
 
- (void)forwardInvocation:(NSInvocation *)invocation
{
    //拿到函數(shù)名
    NSString *key = NSStringFromSelector([invocation selector]);
    if ([key rangeOfString:@"set"].location == 0) {
        //setter函數(shù)形如 setXXX: 拆掉 set和冒號 
        key = [[key substringWithRange:NSMakeRange(3, [key length]-4)] lowercaseString];
        NSString *obj;
        //從參數(shù)列表中找到值
        [invocation getArgument:&obj atIndex:2];
        [data setObject:obj forKey:key];
    } else {
        //getter函數(shù)就相對簡單了,直接把函數(shù)名做 key就好了。
        NSString *obj = [data objectForKey:key];
        [invocation setReturnValue:&obj];
    }
}
 
@end

5 . doesNotRecognizeSelector:

- (void)doesNotRecognizeSelector:(SEL)aSelector

作為找不到函數(shù)實(shí)現(xiàn)的最后一步,NSObject實(shí)現(xiàn)這個函數(shù)只有一個功能,就是拋出異常。
雖然理論上可以重載這個函數(shù)實(shí)現(xiàn)保證不拋出異常(不調(diào)用super實(shí)現(xiàn)),但是蘋果文檔著重提出“一定不能讓這個函數(shù)就這么結(jié)束掉,必須拋出異常”。

使用場景

在一個函數(shù)找不到時,Objective-C提供了三種方式去補(bǔ)救:

  1. 調(diào)用resolveInstanceMethod給個機(jī)會讓類添加這個實(shí)現(xiàn)這個函數(shù)
  2. 調(diào)用forwardingTargetForSelector讓別的對象去執(zhí)行這個函數(shù)
  3. 調(diào)用methodSignatureForSelector(函數(shù)符號制造器)和forwardInvocation(函數(shù)執(zhí)行器)靈活的將目標(biāo)函數(shù)以其他形式執(zhí)行。
    如果都不中,調(diào)用doesNotRecognizeSelector拋出異常。

6 respondsToSelector

+ (BOOL)respondsToSelector:(SEL)aSelector

這個函數(shù)大家再熟悉不過了,用來檢查對象是否實(shí)現(xiàn)了某函數(shù)。

此函數(shù)通常是不需要重載的,但是在動態(tài)實(shí)現(xiàn)了查找過程后,需要重載此函數(shù)讓對外接口查找動態(tài)實(shí)現(xiàn)函數(shù)的時候返回YES,保證對外接口的行為統(tǒng)一。
示例代碼(接forwardInvocation的例子):

@implementation Book
//…
- (BOOL) respondsToSelector:(SEL)aSelector
{
    if (@selector(setTitle:) == aSelector ||
        @selector(title) == aSelector ||
        @selector(setAuthor:) == aSelector ||
        @selector(author) == aSelector)
    {
        return YES;
    }
    
    return [super respondsToSelector: aSelector];
}
 //…
@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,757評論 0 9
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 760評論 0 2
  • 如果想了解Runtime的實(shí)際應(yīng)用請看Runtime全面剖析之簡單使用 一:Runtime簡介二: Runtime...
    iYeso閱讀 817評論 0 2
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,585評論 33 466
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡介 Runt...
    樂樂的簡書閱讀 2,153評論 0 9