衆所周知,Objective-C 是一種運行時語言。運行時怎麼來體現的呢?好比一個對象的類型肯定,或者對象的方法實現的綁定都是推遲到軟件的運行時才能肯定的。而運行時的諸多特性都是由Runtime 來實現的。objective-c
Runtime 其實就是一套C語言API庫,所以它的實現也仍是C語言。若是你想看Runtime的實現源碼,能夠去官網下載:objc4-646.tar.gz(我看的是這個)。數組
本篇不打算介紹objc_msgSend,可是關於OC中的消息最終怎麼被轉化爲objc_msgSend這個過程,仍是有必要找一篇文章好好的看一下。安全
如下內容部分摘錄自:bash
王巍 (@onevcat) 的 深刻Objective-C的動態特性app
Bang 的如何動態調用 C 函數函數
若是你以爲看的不盡興,能夠去看下這兩篇文章。ui
在開始介紹runtime 以前,先講講動態特性。常常被提到和用到的有三種:spa
先來講說動態加載 ,動態加載就是根據需求加載所須要的資源。 有一個典型的例子,就是iPhone 會根據機型的不一樣加載不一樣的圖片。iOS 下通常會有xxx.png、xxx@2x.png、xxx@3x.png,iOS 應用會在非retina設備上加載1倍圖,在retina小尺寸設備(如四、4s、五、5c、5s、六、6s)上加載@2x圖片,而後在大屏retina 設備(如6+、6s+)上加載@3x的圖片。.net
而後再來講說動態類型,即運行時再決定對象的類型。動態類型這個特性在平常開發中很是的常見,最簡單的就是id類型。稍微經常使用的就是某個類和其子類的類型肯定。指針
id類型即通用的對象類,任何對象均可以被id指針所指,而在實際使用中,每每使用introspection來肯定該對象的實際所屬類:
id obj = someInstance;
if ([obj isKindOfClass:someClass])
{
someClass *classSpecifiedInstance = (someClass *)obj;
// Do Something to classSpecifiedInstance which now is an instance of someClass
//...
}
複製代碼
-isMemberOfClass:
是 NSObject
的方法,用以肯定某個 NSObject
對象是不是某個類的成員。與之類似的爲 -isKindOfClass:
,能夠用以肯定某個對象是不是某個類或其子類的成員。這兩個方法爲典型的introspection方法。在肯定對象爲某類成員後,能夠安全地進行強制轉換,繼續以後的工做。
動態類型有利有弊,有了動態類型,咱們能夠在運行時根據對象的類型不一樣執行不一樣的邏輯代碼;可是也致使一些錯誤不能及時的發現。
好比,咱們常常會遇到的這類錯誤:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_NSZeroData count]: unrecognized selector sent to instance 0x7f8632ed7ab0'
複製代碼
這是錯誤的示例代碼:
這就是咱們錯誤的將一個NSData 對象賦值給了NSArray 實例,而後又調用數組的count 方法。在2014 年之前,並不會出現這樣的警告信息,因此那時候很容易出現相似這樣的錯誤。隨着Swift 的推出,OC 中也加入了類型檢查。如今咱們就能夠很及時的減小這類錯誤的產生。基於動態類型,在某個實例對象被肯定後,其類型便被肯定了。該對象對應的屬性和響應的消息也被徹底肯定,這就是動態綁定。在繼續以前,須要明確Objective-C中消息的概念。因爲OC的動態特性,在OC中其實不多說起「函數」的概念,傳統的函數通常在編譯時就已經把參數信息和函數實現打包到編譯後的源碼中了,而在OC中最常使用的是消息機制。調用一個實例的方法,所作的是向該實例的指針發送消息,實例在收到消息後,從自身的實現中尋找響應這條消息的方法。
關於傳統的函數編譯時,把參數信息和函數打包進編譯後的源碼,以及調用過程,能夠參看:Bang的如何動態調用 C 函數 OC 的編譯過程之因此不同,是由於在彙編過程,被蘋果本身寫的彙編接管了。
動態綁定所作的,便是在實例所屬類肯定後,將某些屬性和相應的方法綁定到實例上。這裏所指的屬性和方法固然包括了原來沒有在類中實現的,而是在運行時才須要的新加入的實現。
在Cocoa層,咱們通常向一個NSObject對象發送-respondsToSelector:
或者-instancesRespondToSelector:
等來肯定對象是否能夠對某個SEL作出響應,而在OC消息轉發機制被觸發以前,對應的類的+resolveClassMethod:
和+resolveInstanceMethod:
將會被調用,在此時有機會動態地向類或者實例添加新的方法,也即類的實現是能夠動態綁定的。
一個例子:
void dynamicMethodIMP(id self, SEL _cmd)
{
// implementation ....
}
//該方法在OC消息轉發生效前被調用
+ (BOOL) resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
//向[self class]中新加入返回爲void的實現,SEL名字爲aSEL,實現的具體內容爲dynamicMethodIMP class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, 「v@:」);
return YES;
}
return [super resolveInstanceMethod:aSel];
}
複製代碼
固然也能夠在任意須要的地方調用class_addMethod
或method_setImplementation
(前者添加實現,後者替換實現),來完成動態綁定的需求。
關於動態綁定,個人理解例子是:
假設程序裏有Person這麼一個類,它有name、age、height 以及方法-eat(假設除name、age、height 和-eat 外,其餘的屬性和方法忽略),而後咱們在某處建立了Person這個實例對象。
若是這裏理解的有誤,歡迎指正。剛開始這個實例對象就像白紙同樣乾淨,不知道它的具體類型,也沒有屬性和方法。而後在動態類型階段,肯定它的實際類型。再通過動態綁定,纔會爲其綁定相應的屬性和方法,這時候這個對象纔算完整了。
關於runtime 的一些基礎知識就先到這裏了。