關(guān)于Runtime

一個objc對象如何進行內(nèi)存布局?(考慮有父類的情況)

NSString *str = @“”;

* 所有父類的成員變量和自己的成員變量都會存放在該對象str所對應(yīng)的存儲空間中.

* 每一個對象內(nèi)部都有一個isa指針,指向他的類對象NSString,類對象中存放著本對象的

* 對象方法列表(對象能夠接收的消息列表,保存在它所對應(yīng)的類對象中)

* 成員變量的列表,

* 屬性列表,

* 它內(nèi)部也有一個isa指針指向元對象(meta class),(既類對象所屬的類型,用來表述NSString 是一個什么樣的對象 元對象內(nèi)部存放的是類方法列表)

* 類對象內(nèi)部還有一個superclass的指針,指向他的父類對象。

* NSObject,它的superclass指針指向nil

* 類對象既然稱為對象,那它也是一個實例。類對象中也有一個isa指針指向它的元類(meta class),即類對象是元類的實例。元類內(nèi)部存放的是類方法列表,根元類的isa指針指向自己,superclass指針指向NSObject類。

一個objc對象的isa的指針指向什么?有什么作用?

指向他的類對象,從而可以找到對象上的方法

objc中的類方法和實例方法有什么本質(zhì)區(qū)別和聯(lián)系?

類方法:

1. 類方法是屬于類對象的

2. 類方法只能通過類對象調(diào)用

3. 類方法中的self是類對象

4. 類方法可以調(diào)用其他的類方法

5. 類方法中不能訪問成員變量

6. 類方法中不能直接調(diào)用對象方法

實例方法:

1. 實例方法是屬于實例對象的

2. 實例方法只能通過實例對象調(diào)用

3. 實例方法中的self是實例對象

4. 實例方法中可以訪問成員變量

5. 實例方法中直接調(diào)用實例方法

6. 實例方法中也可以調(diào)用類方法(通過類名)

runtime如何通過selector找到對應(yīng)的IMP地址?(分別考慮類方法和實例方法)?

每一個類對象中都一個方法列表,方法列表中記錄著方法的名稱,方法實現(xiàn),以及參數(shù)類型,其實selector本質(zhì)就是方法名稱,通過這個方法名稱就可以在方法列表中找到對應(yīng)的方法實現(xiàn).

objc中向一個nil對象發(fā)送消息將會發(fā)生什么?

objc在向一個對象發(fā)送消息時,runtime庫會根據(jù)對象的isa指針找到該對象實際所屬的類,然后在該類中的方法列表以及其父類方法列表中尋找方法運行,如果向一個nil對象發(fā)送消息,首先在尋找對象的isa指針時就是0地址返回了,所以不會出現(xiàn)任何錯誤。

objc中向一個對象發(fā)送消息[obj foo]和objc_msgSend()函數(shù)之間有什么關(guān)系?

objc是動態(tài)語言,每個方法在運行時會被動態(tài)轉(zhuǎn)為消息發(fā)送,即:objc_msgSend(receiver, selector)。

[obj foo];在objc動態(tài)編譯時,會被轉(zhuǎn)意為:objc_msgSend(obj, @selector(foo));。

什么時候會報unrecognized selector的異常?

objc在向一個對象發(fā)送消息時,runtime庫會根據(jù)對象的isa指針找到該對象實際所屬的類,然后在該類中的方法列表以及其父類方法列表中尋找方法運行,如果,在最頂層的父類中依然找不到相應(yīng)的方法時,程序在運行時會掛掉并拋出異常unrecognized selector sent to XXX 。但是在這之前,objc的運行時會給出三次拯救程序崩潰的機會:

1. Method resolutionobjc運行時會調(diào)用+resolveInstanceMethod:或者 +resolveClassMethod:,讓你有機會提供一個函數(shù)實現(xiàn)。如果你添加了函數(shù),那運行時系統(tǒng)就會重新啟動一次消息發(fā)送的過程,否則 ,運行時就會移到下一步,消息轉(zhuǎn)發(fā)(Message Forwarding)。

2. Fast forwarding如果目標對象實現(xiàn)了-forwardingTargetForSelector:,Runtime 這時就會調(diào)用這個方法,給你把這個消息轉(zhuǎn)發(fā)給其他對象的機會。既能不能把這條消息轉(zhuǎn)發(fā)給其他接受者處理, 只要這個方法返回的不是nil和self,整個消息發(fā)送的過程就會被重啟,當(dāng)然發(fā)送的對象會變成你返回的那個對象。否則,就會繼續(xù)Normal Fowarding。 這里叫Fast,只是為了區(qū)別下一步的轉(zhuǎn)發(fā)機制。因為這一步不會創(chuàng)建任何新的對象,但下一步轉(zhuǎn)發(fā)會創(chuàng)建一個NSInvocation對象,所以相對更快點。

3. Normal forwarding這一步是Runtime最后一次給你挽救的機會。首先它會發(fā)送-methodSignatureForSelector:消息獲得函數(shù)的參數(shù)和返回值類型。如果-methodSignatureForSelector:返回nil,Runtime則會發(fā)出-doesNotRecognizeSelector:消息,程序這時也就掛掉了。如果返回了一個函數(shù)簽名,Runtime就會創(chuàng)建一個NSInvocation對象并發(fā)送-forwardInvocation:消息給目標對象。

下面的代碼輸出什么?

@implementation Son : Father

- (id)init

{

self = [super init];

if (self) {

NSLog(@"%@", NSStringFromClass([self class]));

NSLog(@"%@", NSStringFromClass([super class]));

}

return self;

}

@end

其實 super 是一個 Magic Keyword, 它本質(zhì)是一個編譯器標示符,和 self 是指向的同一個消息接受者!他們兩個的不同點在于:super 會告訴編譯器,調(diào)用 class 這個方法時,要去父類的方法,而不是本類里的。

上面的例子不管調(diào)用[self class]還是[super class],接受消息的對象都是當(dāng)前 Son *xxx 這個對象。

當(dāng)使用 self 調(diào)用方法時,會從當(dāng)前類的方法列表中開始找,如果沒有,就從父類中再找;而當(dāng)使用 super 時,則從父類的方法列表中開始找。然后調(diào)用父類的這個方法。

_objc_msgForward函數(shù)是做什么的,直接調(diào)用它將會發(fā)生什么?

_objc_msgForward是 IMP 類型,用于消息轉(zhuǎn)發(fā)的:當(dāng)向一個對象發(fā)送一條消息,但它并沒有實現(xiàn)的時候,_objc_msgForward會嘗試做消息轉(zhuǎn)發(fā)。

結(jié)合《NSObject官方文檔》,排除掉 NSObject 做的事,剩下的就是_objc_msgForward消息轉(zhuǎn)發(fā)做的幾件事:

1. 調(diào)用resolveInstanceMethod:方法 (或 resolveClassMethod:)。允許用戶在此時為該 Class 動態(tài)添加實現(xiàn)。如果有實現(xiàn)了,則調(diào)用并返回YES,那么重新開始objc_msgSend流程。這一次對象會響應(yīng)這個選擇器,一般是因為它已經(jīng)調(diào)用過class_addMethod。如果仍沒實現(xiàn),繼續(xù)下面的動作。

2. 調(diào)用forwardingTargetForSelector:方法,嘗試找到一個能響應(yīng)該消息的對象。如果獲取到,則直接把消息轉(zhuǎn)發(fā)給它,返回非 nil 對象。否則返回 nil ,繼續(xù)下面的動作。注意,這里不要返回 self ,否則會形成死循環(huán)。

3. 調(diào)用methodSignatureForSelector:方法,嘗試獲得一個方法簽名。如果獲取不到,則直接調(diào)用doesNotRecognizeSelector拋出異常。如果能獲取,則返回非nil:創(chuàng)建一個 NSlnvocation 并傳給forwardInvocation:。

4. 調(diào)用forwardInvocation:方法,將第3步獲取到的方法簽名包裝成 Invocation 傳入,如何處理就在這里面了,并返回非ni。

5. 調(diào)用doesNotRecognizeSelector: ,默認的實現(xiàn)是拋出異常。如果第3步?jīng)]能獲得一個方法簽名,執(zhí)行該步驟。

一旦調(diào)用_objc_msgForward,將跳過查找 IMP 的過程,直接觸發(fā)“消息轉(zhuǎn)發(fā)”,

如果調(diào)用了_objc_msgForward,即使這個對象確實已經(jīng)實現(xiàn)了這個方法,你也會告訴objc_msgSend:“我沒有在這個對象里找到這個方法的實現(xiàn)”

有哪些場景需要直接調(diào)用_objc_msgForward?最常見的場景是:你想獲取某方法所對應(yīng)的NSInvocation對象。舉例說明:

JSPatch (Github 鏈接)就是直接調(diào)用_objc_msgForward來實現(xiàn)其核心功能的:

JSPatch 以小巧的體積做到了讓JS調(diào)用/替換任意OC方法,讓iOS APP具備熱更新的能力。

能否向編譯后得到的類中增加實例變量?能否向運行時創(chuàng)建的類中添加實例變量?為什么?

* 不能向編譯后得到的類中增加實例變量;

* 能向運行時創(chuàng)建的類中添加實例變量;

解釋下:

* 因為編譯后的類已經(jīng)注冊在 runtime 中,類結(jié)構(gòu)體中的 objc_ivar_list 實例變量的鏈表 和 instance_size 實例變量的內(nèi)存大小已經(jīng)確定,同時runtime 會調(diào)用 class_setIvarLayout 或 class_setWeakIvarLayout 來處理 strong weak 引用。所以不能向存在的類中添加實例變量;

* 運行時創(chuàng)建的類是可以添加實例變量,調(diào)用 class_addIvar 函數(shù)。但是得在調(diào)用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。

http://www.itdecent.cn/p/e071206103a4 runtime 的使用場景

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,058評論 0 9
  • 1.理解NSObject和元類 1.1 在OC中的對象和類是什么 對象是在objc.h中定義的 類是在runtim...
    HWenj閱讀 982評論 0 3
  • 原文出處:南峰子的技術(shù)博客 Objective-C語言是一門動態(tài)語言,它將很多靜態(tài)語言在編譯和鏈接時期做的事放到了...
    _燴面_閱讀 1,419評論 1 5
  • 文中的實驗代碼我放在了這個項目中。 以下內(nèi)容是我通過整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 1,028評論 0 6
  • 本文詳細整理了 Cocoa 的 Runtime 系統(tǒng)的知識,它使得 Objective-C 如虎添翼,具備了靈活的...
    lylaut閱讀 867評論 0 4

友情鏈接更多精彩內(nèi)容