一個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 的使用場景