一個(gè)objc對象如何進(jìn)行內(nèi)存布局炮姨?(考慮有父類的情況)
NSString *str = @“”;
* 所有父類的成員變量和自己的成員變量都會存放在該對象str所對應(yīng)的存儲空間中.
* 每一個(gè)對象內(nèi)部都有一個(gè)isa指針,指向他的類對象NSString,類對象中存放著本對象的
* 對象方法列表(對象能夠接收的消息列表千康,保存在它所對應(yīng)的類對象中)
* 成員變量的列表,
* 屬性列表,
* 它內(nèi)部也有一個(gè)isa指針指向元對象(meta class),(既類對象所屬的類型,用來表述NSString 是一個(gè)什么樣的對象 元對象內(nèi)部存放的是類方法列表)
* 類對象內(nèi)部還有一個(gè)superclass的指針,指向他的父類對象。
* NSObject乔妈,它的superclass指針指向nil
* 類對象既然稱為對象,那它也是一個(gè)實(shí)例。類對象中也有一個(gè)isa指針指向它的元類(meta class)欣除,即類對象是元類的實(shí)例。元類內(nèi)部存放的是類方法列表挪略,根元類的isa指針指向自己历帚,superclass指針指向NSObject類。
一個(gè)objc對象的isa的指針指向什么杠娱?有什么作用挽牢?
指向他的類對象,從而可以找到對象上的方法
objc中的類方法和實(shí)例方法有什么本質(zhì)區(qū)別和聯(lián)系?
類方法:
1. 類方法是屬于類對象的
2. 類方法只能通過類對象調(diào)用
3. 類方法中的self是類對象
4. 類方法可以調(diào)用其他的類方法
5. 類方法中不能訪問成員變量
6. 類方法中不能直接調(diào)用對象方法
實(shí)例方法:
1. 實(shí)例方法是屬于實(shí)例對象的
2. 實(shí)例方法只能通過實(shí)例對象調(diào)用
3. 實(shí)例方法中的self是實(shí)例對象
4. 實(shí)例方法中可以訪問成員變量
5. 實(shí)例方法中直接調(diào)用實(shí)例方法
6. 實(shí)例方法中也可以調(diào)用類方法(通過類名)
runtime如何通過selector找到對應(yīng)的IMP地址摊求?(分別考慮類方法和實(shí)例方法)禽拔?
每一個(gè)類對象中都一個(gè)方法列表,方法列表中記錄著方法的名稱,方法實(shí)現(xiàn),以及參數(shù)類型,其實(shí)selector本質(zhì)就是方法名稱,通過這個(gè)方法名稱就可以在方法列表中找到對應(yīng)的方法實(shí)現(xiàn).
objc中向一個(gè)nil對象發(fā)送消息將會發(fā)生什么?
objc在向一個(gè)對象發(fā)送消息時(shí)室叉,runtime庫會根據(jù)對象的isa指針找到該對象實(shí)際所屬的類睹栖,然后在該類中的方法列表以及其父類方法列表中尋找方法運(yùn)行,如果向一個(gè)nil對象發(fā)送消息茧痕,首先在尋找對象的isa指針時(shí)就是0地址返回了野来,所以不會出現(xiàn)任何錯誤。
objc中向一個(gè)對象發(fā)送消息[obj foo]和objc_msgSend()函數(shù)之間有什么關(guān)系踪旷?
objc是動態(tài)語言曼氛,每個(gè)方法在運(yùn)行時(shí)會被動態(tài)轉(zhuǎn)為消息發(fā)送豁辉,即:objc_msgSend(receiver, selector)。
[obj foo];在objc動態(tài)編譯時(shí)舀患,會被轉(zhuǎn)意為:objc_msgSend(obj, @selector(foo));徽级。
什么時(shí)候會報(bào)unrecognized selector的異常?
objc在向一個(gè)對象發(fā)送消息時(shí)聊浅,runtime庫會根據(jù)對象的isa指針找到該對象實(shí)際所屬的類餐抢,然后在該類中的方法列表以及其父類方法列表中尋找方法運(yùn)行,如果狗超,在最頂層的父類中依然找不到相應(yīng)的方法時(shí)弹澎,程序在運(yùn)行時(shí)會掛掉并拋出異常unrecognized selector sent to XXX 。但是在這之前努咐,objc的運(yùn)行時(shí)會給出三次拯救程序崩潰的機(jī)會:
1. Method resolutionobjc運(yùn)行時(shí)會調(diào)用+resolveInstanceMethod:或者 +resolveClassMethod:苦蒿,讓你有機(jī)會提供一個(gè)函數(shù)實(shí)現(xiàn)。如果你添加了函數(shù)渗稍,那運(yùn)行時(shí)系統(tǒng)就會重新啟動一次消息發(fā)送的過程佩迟,否則 ,運(yùn)行時(shí)就會移到下一步竿屹,消息轉(zhuǎn)發(fā)(Message Forwarding)报强。
2. Fast forwarding如果目標(biāo)對象實(shí)現(xiàn)了-forwardingTargetForSelector:,Runtime 這時(shí)就會調(diào)用這個(gè)方法拱燃,給你把這個(gè)消息轉(zhuǎn)發(fā)給其他對象的機(jī)會秉溉。既能不能把這條消息轉(zhuǎn)發(fā)給其他接受者處理, 只要這個(gè)方法返回的不是nil和self碗誉,整個(gè)消息發(fā)送的過程就會被重啟召嘶,當(dāng)然發(fā)送的對象會變成你返回的那個(gè)對象。否則哮缺,就會繼續(xù)Normal Fowarding弄跌。 這里叫Fast,只是為了區(qū)別下一步的轉(zhuǎn)發(fā)機(jī)制尝苇。因?yàn)檫@一步不會創(chuàng)建任何新的對象铛只,但下一步轉(zhuǎn)發(fā)會創(chuàng)建一個(gè)NSInvocation對象,所以相對更快點(diǎn)糠溜。
3. Normal forwarding這一步是Runtime最后一次給你挽救的機(jī)會淳玩。首先它會發(fā)送-methodSignatureForSelector:消息獲得函數(shù)的參數(shù)和返回值類型。如果-methodSignatureForSelector:返回nil非竿,Runtime則會發(fā)出-doesNotRecognizeSelector:消息凯肋,程序這時(shí)也就掛掉了。如果返回了一個(gè)函數(shù)簽名汽馋,Runtime就會創(chuàng)建一個(gè)NSInvocation對象并發(fā)送-forwardInvocation:消息給目標(biāo)對象侮东。
下面的代碼輸出什么?
@implementation Son : Father
- (id)init
{
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
其實(shí) super 是一個(gè) Magic Keyword豹芯, 它本質(zhì)是一個(gè)編譯器標(biāo)示符悄雅,和 self 是指向的同一個(gè)消息接受者!他們兩個(gè)的不同點(diǎn)在于:super 會告訴編譯器铁蹈,調(diào)用 class 這個(gè)方法時(shí)宽闲,要去父類的方法,而不是本類里的握牧。
上面的例子不管調(diào)用[self class]還是[super class]容诬,接受消息的對象都是當(dāng)前 Son *xxx 這個(gè)對象。
當(dāng)使用 self 調(diào)用方法時(shí)沿腰,會從當(dāng)前類的方法列表中開始找览徒,如果沒有,就從父類中再找颂龙;而當(dāng)使用 super 時(shí)习蓬,則從父類的方法列表中開始找。然后調(diào)用父類的這個(gè)方法措嵌。
_objc_msgForward函數(shù)是做什么的躲叼,直接調(diào)用它將會發(fā)生什么?
_objc_msgForward是 IMP 類型企巢,用于消息轉(zhuǎn)發(fā)的:當(dāng)向一個(gè)對象發(fā)送一條消息枫慷,但它并沒有實(shí)現(xiàn)的時(shí)候,_objc_msgForward會嘗試做消息轉(zhuǎn)發(fā)浪规。
結(jié)合《NSObject官方文檔》或听,排除掉 NSObject 做的事,剩下的就是_objc_msgForward消息轉(zhuǎn)發(fā)做的幾件事:
1. 調(diào)用resolveInstanceMethod:方法 (或 resolveClassMethod:)罗丰。允許用戶在此時(shí)為該 Class 動態(tài)添加實(shí)現(xiàn)神帅。如果有實(shí)現(xiàn)了,則調(diào)用并返回YES萌抵,那么重新開始o(jì)bjc_msgSend流程找御。這一次對象會響應(yīng)這個(gè)選擇器,一般是因?yàn)樗呀?jīng)調(diào)用過class_addMethod绍填。如果仍沒實(shí)現(xiàn)霎桅,繼續(xù)下面的動作。
2. 調(diào)用forwardingTargetForSelector:方法讨永,嘗試找到一個(gè)能響應(yīng)該消息的對象滔驶。如果獲取到,則直接把消息轉(zhuǎn)發(fā)給它卿闹,返回非 nil 對象揭糕。否則返回 nil 萝快,繼續(xù)下面的動作。注意著角,這里不要返回 self 揪漩,否則會形成死循環(huán)。
3. 調(diào)用methodSignatureForSelector:方法吏口,嘗試獲得一個(gè)方法簽名奄容。如果獲取不到,則直接調(diào)用doesNotRecognizeSelector拋出異常产徊。如果能獲取昂勒,則返回非nil:創(chuàng)建一個(gè) NSlnvocation 并傳給forwardInvocation:。
4. 調(diào)用forwardInvocation:方法舟铜,將第3步獲取到的方法簽名包裝成 Invocation 傳入戈盈,如何處理就在這里面了,并返回非ni深滚。
5. 調(diào)用doesNotRecognizeSelector: 奕谭,默認(rèn)的實(shí)現(xiàn)是拋出異常。如果第3步?jīng)]能獲得一個(gè)方法簽名痴荐,執(zhí)行該步驟血柳。
一旦調(diào)用_objc_msgForward,將跳過查找 IMP 的過程生兆,直接觸發(fā)“消息轉(zhuǎn)發(fā)”难捌,
如果調(diào)用了_objc_msgForward,即使這個(gè)對象確實(shí)已經(jīng)實(shí)現(xiàn)了這個(gè)方法鸦难,你也會告訴objc_msgSend:“我沒有在這個(gè)對象里找到這個(gè)方法的實(shí)現(xiàn)”
有哪些場景需要直接調(diào)用_objc_msgForward根吁?最常見的場景是:你想獲取某方法所對應(yīng)的NSInvocation對象。舉例說明:
JSPatch (Github 鏈接)就是直接調(diào)用_objc_msgForward來實(shí)現(xiàn)其核心功能的:
JSPatch 以小巧的體積做到了讓JS調(diào)用/替換任意OC方法合蔽,讓iOS APP具備熱更新的能力击敌。
能否向編譯后得到的類中增加實(shí)例變量?能否向運(yùn)行時(shí)創(chuàng)建的類中添加實(shí)例變量拴事?為什么沃斤?
* 不能向編譯后得到的類中增加實(shí)例變量;
* 能向運(yùn)行時(shí)創(chuàng)建的類中添加實(shí)例變量刃宵;
解釋下:
* 因?yàn)榫幾g后的類已經(jīng)注冊在 runtime 中衡瓶,類結(jié)構(gòu)體中的 objc_ivar_list 實(shí)例變量的鏈表 和 instance_size 實(shí)例變量的內(nèi)存大小已經(jīng)確定,同時(shí)runtime 會調(diào)用 class_setIvarLayout 或 class_setWeakIvarLayout 來處理 strong weak 引用牲证。所以不能向存在的類中添加實(shí)例變量哮针;
* 運(yùn)行時(shí)創(chuàng)建的類是可以添加實(shí)例變量,調(diào)用 class_addIvar 函數(shù)。但是得在調(diào)用 objc_allocateClassPair 之后十厢,objc_registerClassPair 之前等太,原因同上。
http://www.reibang.com/p/e071206103a4 runtime 的使用場景