iOS-Runtime、對象模型、消息轉發

Objective-C只是在C語言層面上加了些關鍵字和語法。真正讓Objective-C如此強大的是它的運行時。它很小但卻很強大。它的核心是消息分發。html

Messageios

  執行一個方法,有些語言、編譯器會執行一些額外的優化和錯誤檢查,由於調用的關係很直接也很明顯。可是對於消息分發來講,就不必定了。在發消息前沒必要知道某個對象是否能處理消息,你把消息發給它,它可能會處理,也可能會交給其餘的objec 處理。一個消息不用對應一個方法、一個對象也可能實現一個方法來處理多條消息。objective-c

在objcetive中,消息是經過objc_msgSend()這個runtime實現的。編譯器把消息的分發轉變成objc_msgSend執行。緩存

id returnValue = [someobject messagename:parameter];

其中someObject是接收者(receiver),messagename叫作Selector,Selector和參數合起來叫作消息.函數

objc_msgSend(id self, sel cmd,...)

第一個參數表明接收者,第二個參數是Selector,後面的參數就是消息中得參數,位置順序不變。因此根據上面的原型,咱們能夠把函數改寫成這樣:優化

id returnValue = objc_msgsend(someobject,@selector(messagename:),parameter);

  objc_msgsend函數會根據接受者和selector的類型調用適當的方法。會在接收者所屬的類裏面搜尋「方法列表」(list of method).若是能找到與selector名稱相符合的方法,就跳轉至實現代碼。若是找不到的話,就會向上查找,等找到合適的方法後跳轉。若是仍是找不到得話就會執行「消息轉發」的操做。spa

  按照這個思路,調用一個方法須要不少步驟。可是objc_msgsend會將結果緩存到快速映射表(fast map)裏面,每一個類都有一個這樣的緩存,如果稍後還向該類發送相同的消息的話,執行起來就很快了。3d

對象某型

打開NSObject.h能夠看見下面object_class的組成指針

打開runtime.h能夠看見下面object_class的組成調試

 

isa指針:每一個對象都是類的實例,isa指針指向這個實例所屬的類,每一個類也是一個對象,類也有isa指針。

super_class:父類

name:類的名字

info:類的一些信息

instance_size:實例的大小

objc_ivar_list:實例的參數列表

objc_method_list:實例的方法列表

objc_cache:方法的緩存

objc_protocol_list:協議方法列表

 

借用網上的一張圖:圖片來自這裏

 

根據這張圖片,能夠發現有如下信息:

(1)類也是一個對象,這個對象是另一個類的實例,這個類是meta(元類)

  (2) 每一個meta類也是一個對象,分別指向根meta類。

(3)根元類的isa指針指向本身,造成了一個閉環。

(4)在繼承關係中,因爲類方法的定義是保存在元類(metaclass)中,而方法調用的規則是,若是該類沒有一個方法的實現,則向它的父類繼續查找。因此,爲了保證父類的類方法能夠在子類中能夠被調用,因此子類的元類會繼承父類的元類,換而言之,類對象和元類對象有着一樣的繼承關係。

Method Swizzling

Method Swizzling 能夠交換兩個方法的實現。爲何會有這樣的功能呢?首先看看擴展類的兩種途徑,第一種是子類化,重寫父類的方法,而後調用父類的實現。可是使用子類的過程當中,若是返回的時父類的類型的話怎麼辦?能夠使用Category,添加一個擴展方法,這個方法若是沒有和系統調用的方法重名的話通常狀況下是沒有問題的,可是若是重寫了系統的方法的話,那麼就永遠不能調用這個方法了。因此Method Swizzling這個方法能解決這些問題,技能擴展類,又還能調用原來類的實現,一般狀況下先創建一個與系統對應的擴展類,而後經過method_exchangeImplementations方法交換它們的實現。

首先定義一個NSString的擴展類

@interface NSString (Addition)
- (NSString *)test_myLowerString;
@end
@implementation NSString (Addition)

- (NSString *)test_myLowerString
{
    NSString *lowercase = [self test_myLowerString];
    NSLog(@"%@ =>%@",self,lowercase);
    return lowercase;
}

@end

而後交換它們的實現方法

    NSString *testString = @"TEST";
    NSLog(@"%@,",[testString lowercaseString]);
    
    Method originMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));
    Method swapMethod = class_getInstanceMethod([NSString class], @selector(test_myLowerString));
    method_exchangeImplementations(originMethod, swapMethod);
    NSLog(@"%@",[testString lowercaseString]);

打印的結果是:

因此咱們改寫了系統的lowercaseString方法,每當咱們調用擴展的test_myLowerString方法時候,實際上是調用系統的lowercaseString方法,這種作法通常狀況用在調試系統,不過最好不建議改寫系統的一些方法,可能會帶來不可調試的後果,因此使用前需慎重。

動態方法處理和消息轉發

上面談了方法的交換,是對消息處理的一種,下面再談另一種方法的處理

 

參考連接

 http://blog.devtang.com/blog/2013/10/15/objective-c-object-model/

http://limboy.me/ios/2013/08/03/dynamic-tips-and-tricks-with-objective-c.html

相關文章
相關標籤/搜索