Objective-C runtime初識

Objective-C Runtime

Describes the macOS Objective-C runtime library support functions and data structures.

Overview(概述)

以下是官方文檔中對Runtime給出的定義
The Objective-C runtime is a runtime library that provides support for the dynamic properties of the Objective-C language, and as such is linked to by all Objective-C apps.
正式runtime這一個庫給予了Objective-C language動態(tài)的屬性, 所有的OC App都可以直接使用它

You typically don't need to use the Objective-C runtime library directly when programming in Objective-C. This API is useful primarily for developing bridge layers between Objective-C and other languages, or for low-level debugging.
一般programming時不會直接使用到runtime庫, runtime這一功能或者屬性一般用在跨域語言編程, 或者在較底層的debug中
Note

All char * in the runtime API should be considered to have UTF-8 encoding.
在runtime API中所有char類型都是以UTF-8編碼的

以上是文檔中對runtime做的一些簡單介紹

經過之前看過的其他人對runtime的經驗總結和自己的實踐, 目前對Runtime的概念:

消息動態(tài)解析
消息重定向
消息轉發(fā)

動態(tài)解析 在運行時(程序運行中)動態(tài)地:
給類中的已經定義但尚未實現(xiàn)的方法, 動態(tài)地綁定實現(xiàn)方法
給類增加或綁定既未定義也未實現(xiàn)方法, 說簡單就是給類增加方法

文檔中接下來是runtime方法的介紹, 我們在暫停在這里 先對上面幾個概念做一個簡單的說明

在之前必要我們先來看下[receiver message];這句話的實現(xiàn)過程, 也就是消息機制是如何在運作的

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;

每一個NSObject對象都有成員變量列表, 方法列表, 緩存, 接口列表
方法列表中存儲方法的指針(IMP)
緩存中存儲的是曾經被調用的方法

[receiver message];會被轉換成消息發(fā)送的模式:

id objc_msgSend(id self, SEL _cmd, …);

當對象接收到消息時會按照以下順序依次檢查, 在任何一個環(huán)節(jié)如果被響應則結束 否則報錯
-> 對象接收到消息
-> 查看緩存中是否有匹配的方法, 如果有則響應 否則繼續(xù)
-> 查看方法列表中是否有匹配的方法, 如果有則響應 否則繼續(xù)
-> 查看父類中是否有匹配的方法, 如果有則響應 否則繼續(xù)
->進入動態(tài)解析 + (BOOL)resolveInstanceMethod:(SEL)sel, 如果有指定動態(tài)解析方法則響應 否則繼續(xù)
->進入消息重定向 - (id)forwardingTargetForSelector:(SEL)aSelector, 如果有指定消息接收對象則將消息轉由接收對象響應 否則繼續(xù)
->開始消息轉發(fā) - (void)forwardInvocation:(NSInvocation *)anInvocation, 如果有指定轉發(fā)對象則轉發(fā)給該對象響應, 否則拋出異常

再消息轉發(fā)前我們有兩次機會來修改或者設定對象方法的實現(xiàn)

下面再逐一說說
動態(tài)解析
假如我們有一個ClassA, 在它的頭文件中我們定義了一個- (void)printName;方法, 但我們并沒有在.m文件中讓它實現(xiàn)
如果我們直接在Viewcontroller中使用[[ClassA new] printName];程序不會出錯 但也不會做任何事情
我們可以重寫resolveInstanceMethod:或者resolveClassMethod:方法, 在這里我們給printName方法添加實現(xiàn)

/**
 要動態(tài)綁定的方法

 @param self 要綁定方法的對象
 @param _cmd 方法信息
 */
void dynamicMethodIMP(id self, SEL _cmd) {
    
    NSLog(@"SEL: %s method is added", sel_getName(_cmd));
    NSLog(@"Name: Jackey");
}


/**
 動態(tài)綁定和解析方法

 @param sel 方法信息
 @return 是否已經處理該方法
 */
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    
    NSLog(@"SEL: %s method does not exist", sel_getName(sel));
    
    if (sel == @selector(printName)) {
        
        class_addMethod ([self class], sel, (IMP) dynamicMethodIMP, "v@:");
        return YES;
    }
    
    return [super resolveInstanceMethod:sel];
}

這樣我們再運行[[ClassA new] printName];就會輸出Name: Jackey

重定向:

如果經過動態(tài)解析后, 消息還沒有被響應就會進入到重定向階段

我們可以重寫- (id)forwardingTargetForSelector:(SEL)aSelector將消息重定向給可以響應的對象

/**
 方法重定向

 @param aSelector 方法信息
 @return 返回重定向后要相應的對象
 */
- (id)forwardingTargetForSelector:(SEL)aSelector {
    
    NSLog(@"Current class can't response to SEL: %s", sel_getName(aSelector));
    
    if (aSelector == @selector(printRightName)) {
        
        NSLog(@"Forward to target: %@", [ClassB class]);
        return [ClassB new];
    }
    
    return [super forwardingTargetForSelector:aSelector];
    
}

最后如果前面都沒有處理就會進入到消息轉發(fā), 我們可以通過重寫

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector;

- (void)forwardInvocation:(NSInvocation *)anInvocation;
/**
 轉發(fā)前, 獲取方法簽名

 @param selector 方法信息
 @return NSInvocation消息對象
 */
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    
    NSString *sel = NSStringFromSelector(selector);
    if ([sel rangeOfString:@"set"].location == 0){
        
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    }
    else{
        
        return [NSMethodSignature signatureWithObjCTypes:"@@:"];
    }
}

/**
 轉發(fā)

 @param anInvocation 消息對象
 */
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    
    NSLog(@"No class can't response to SEL: %s", sel_getName([anInvocation selector]));
    
    ClassC *c = [ClassC new];
    if ([c respondsToSelector:[anInvocation selector]]) {
        
        NSLog(@"method apply deliver to %@", [ClassC class]);
        [anInvocation invokeWithTarget:c];
    }
    
    else {
        
        [super forwardInvocation:anInvocation];
    }
}

消息的轉發(fā)彌補了OC不能多繼承的問題

最后我們來看下Method Swizzling

我們可以直接修改方法的指針, 讓一個方法名指向其他的方法實現(xiàn)

Method ori_method = class_getInstanceMethod([ClassB class], @selector(printRightName));
Method my_method  = class_getInstanceMethod([ClassC class], @selector(printFamilyName));
    
method_exchangeImplementations(ori_method, my_method);
    
[[ClassB new] printRightName];

使用method_exchangeImplementation交換了兩個對象方法的指針

printRightName執(zhí)行的實際是printFamilyName

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市及刻,隨后出現(xiàn)的幾起案子镀裤,更是在濱河造成了極大的恐慌,老刑警劉巖缴饭,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件暑劝,死亡現(xiàn)場離奇詭異,居然都是意外死亡颗搂,警方通過查閱死者的電腦和手機担猛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人傅联,你說我怎么就攤上這事先改。” “怎么了蒸走?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵仇奶,是天一觀的道長。 經常有香客問我比驻,道長该溯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任别惦,我火速辦了婚禮朗伶,結果婚禮上,老公的妹妹穿的比我還像新娘步咪。我一直安慰自己,他們只是感情好益楼,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布猾漫。 她就那樣靜靜地躺著,像睡著了一般感凤。 火紅的嫁衣襯著肌膚如雪悯周。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天陪竿,我揣著相機與錄音禽翼,去河邊找鬼。 笑死族跛,一個胖子當著我的面吹牛闰挡,可吹牛的內容都是我干的。 我是一名探鬼主播礁哄,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼长酗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了桐绒?” 一聲冷哼從身側響起夺脾,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茉继,沒想到半個月后咧叭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡烁竭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年菲茬,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡生均,死狀恐怖听想,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情马胧,我是刑警寧澤汉买,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站佩脊,受9級特大地震影響蛙粘,放射性物質發(fā)生泄漏。R本人自食惡果不足惜威彰,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一出牧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧歇盼,春花似錦舔痕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至邢笙,卻和暖如春啸如,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背氮惯。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工叮雳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人妇汗。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓帘不,卻偏偏與公主長得像,于是被迫代替她去往敵國和親杨箭。 傳聞我的和親對象是個殘疾皇子厌均,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內容

  • 本文詳細整理了 Cocoa 的 Runtime 系統(tǒng)的知識,它使得 Objective-C 如虎添翼告唆,具備了靈活的...
    lylaut閱讀 800評論 0 4
  • 轉載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 733評論 0 2
  • 我們常常會聽說 Objective-C 是一門動態(tài)語言棺弊,那么這個「動態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,193評論 0 7
  • 文中的實驗代碼我放在了這個項目中擒悬。 以下內容是我通過整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 923評論 0 6
  • 好像在學校過幾時到i need someone with
    無與倫比的阿倫閱讀 140評論 0 0