1、什么是runtime
runtime又叫運(yùn)行時(shí)拍屑,將數(shù)據(jù)類型的確定由編譯時(shí)推遲到了運(yùn)行時(shí) 是一套比較底層的純C語(yǔ)言API??OC代碼在運(yùn)行過(guò)程中會(huì)被轉(zhuǎn)成runtime的C語(yǔ)言代碼途戒,可以用于在程序的運(yùn)行過(guò)程中動(dòng)態(tài)的創(chuàng)建類,??動(dòng)態(tài)添加、修改類的方法和屬性負(fù)責(zé)消息的傳遞和轉(zhuǎn)發(fā)查找類中的成員僵驰、屬性喷斋、方法
2、Runtime的數(shù)據(jù)模型是怎樣的
主要objc_object蒜茴、 objc_class星爪、 isa指針、 method_t 幾個(gè)重要部分組成
objc_object:
平時(shí)我們所使用的所有對(duì)象都是ID類型的粉私, ID類型的對(duì)象對(duì)應(yīng)到runtime當(dāng)中實(shí)際上代表的就是objc_object結(jié)構(gòu)體 主要包含
isa_t (共用體)
關(guān)于isa指針操作 (比如通過(guò)objc_objec這個(gè)結(jié)構(gòu)體獲取它的isa所指向的類對(duì)象顽腾,包括類對(duì)象的isa指針指向的元類對(duì)象)
弱引用相關(guān)的方法 (比如標(biāo)記一個(gè)對(duì)象是否有弱引用指針)
關(guān)聯(lián)對(duì)象相關(guān)的方法 (比如這個(gè)對(duì)象我們?yōu)樗O(shè)置了關(guān)聯(lián)屬性, 關(guān)聯(lián)對(duì)象的一些方法也體現(xiàn)在objc_object)
內(nèi)存管理 (retain/release/autoreleasePool)
objc_class:
在OC語(yǔ)言當(dāng)中所使用的Class 對(duì)應(yīng)到runtime當(dāng)中就是objc_class 它也是一個(gè)結(jié)構(gòu)體
objc_class繼承自objc_object, objc_object中的isa指針指向objc_class類對(duì)象或元類對(duì)象
objc_class 由 superClass诺核、 cache_t抄肖、 class_data_bits_t 3個(gè)結(jié)構(gòu)體組成
superClass指針:指向的類型也是一個(gè)Class,如果是類對(duì)象則指向當(dāng)前類的父類
cache_t: 主要用于消息傳遞當(dāng)中緩存方法的查找窖杀, 對(duì)應(yīng)了裝滿bucket_t數(shù)據(jù)結(jié)構(gòu)的哈希表
class_data_bits_t: 它是對(duì)class_rw_t結(jié)構(gòu)體的一層封裝漓摩,主要用于表達(dá)類的基本信息,比如成員變量入客、屬性管毙、方法列表 包括分類的添加
class_rw_t: 代表了類相關(guān)的讀寫(xiě)信息 是對(duì)class_ro_t的一層封裝, 主要包含了
class_ro_t
properites
procotols
methods
class_ro_t:這個(gè)結(jié)構(gòu)體中包含了
name
ivar
properites
procotols
methodList
isa指針:
在oc當(dāng)中命名為isa_t共用體 是C++類型的空用體
指針類isa: isa的值代表Class地址
非指針型isa: isa的值部分代表Class地址
如果是對(duì)象則指向其類對(duì)象, 如果是類對(duì)象則指向其元類對(duì)象
實(shí)例 ---- isa ---- class
CLass --- isa ---- MetaClass
method_t:
1
2
3
3痊项、cache_t的作用是什么锅风, 具有什么特點(diǎn)
在消息傳遞的過(guò)程中用于快速查找方法執(zhí)行函數(shù),如果有一個(gè)緩存就不用到它的方法列表中去查找鞍泉,可以提高消息傳遞效率
是可增量擴(kuò)展的哈希表結(jié)構(gòu) //當(dāng)我們存儲(chǔ)的這個(gè)結(jié)構(gòu)增大會(huì)逐漸的增大內(nèi)存空間
是局部性原理的最佳應(yīng)用
4皱埠、什么是 bucket_t (KEY IMP),如何定位bucket_t 如何進(jìn)行緩存查找
bucket_t是以哈希表方式存儲(chǔ)方法列表的,??通過(guò)key->value直接進(jìn)行訪問(wèn)的數(shù)據(jù)結(jié)構(gòu)
以SEL為key咖驮,_imp為value來(lái)存儲(chǔ)方法?
它通過(guò)把關(guān)鍵碼值映射到表中一個(gè)位置來(lái)訪問(wèn)記錄边器,以加快查找的速度
5训枢、類對(duì)象、元類對(duì)象分別是什么
類對(duì)象與元類對(duì)象都是objc_class數(shù)據(jù)結(jié)構(gòu)的忘巧,這個(gè)數(shù)據(jù)結(jié)構(gòu)繼承了objc_object所有他們都有isa指針
類對(duì)象存儲(chǔ)實(shí)例方法列表等信息恒界,??
元類對(duì)象存儲(chǔ)類表方列表法等信息??
6、如何找到類對(duì)象元類對(duì)象
實(shí)例對(duì)象可以通過(guò)isa指針找到它的類對(duì)象
類對(duì)象可以通過(guò)isa指針找到它的元類對(duì)象
7砚嘴、元類對(duì)象的指針指向哪里
????1十酣、任何一個(gè)元類對(duì)象它的isa指針都指向根元類對(duì)象
8、如果我們調(diào)用的類方法沒(méi)有實(shí)現(xiàn) 但是有同名的實(shí)例方法實(shí)現(xiàn)會(huì)不會(huì)產(chǎn)生崩潰或?qū)嶋H調(diào)用
不會(huì)奔潰
由于根元類對(duì)象的superClass指針指向了根類對(duì)象际长,當(dāng)我們?cè)谠悓?duì)象找類方找不到的時(shí)候會(huì)逐級(jí)向上查找 如果有同名的實(shí)例方法會(huì)調(diào)用
9耸采、class是否是對(duì)象?? ? ? ? ? ??
Class是對(duì)象,我們稱為類對(duì)象
在OC語(yǔ)言當(dāng)中所使用的Class 對(duì)應(yīng)到runtime當(dāng)中就是objc_class 它也是一個(gè)結(jié)構(gòu)體 工育,objc_class 繼承自 objc_object
10虾宇、***?消息傳遞的過(guò)程 ?
首先調(diào)用方法的時(shí)候會(huì)使用哈希方法查找 緩存中是否命中,看看緩存中是否有對(duì)應(yīng)的選擇器名稱的方法實(shí)現(xiàn)如绸,如果有就通過(guò)函數(shù)指針調(diào)用函數(shù)
如果沒(méi)有則通過(guò)當(dāng)前實(shí)例的isa指針查找當(dāng)前類對(duì)象的方法列表嘱朽,如果找到則根據(jù)函數(shù)指針進(jìn)行調(diào)用 (如果方法列表已排序則使用二分查找,如果沒(méi)有排序則使用遍歷查找)
如果在當(dāng)前類對(duì)象方法列表中沒(méi)有找到就會(huì)逐級(jí)在父類方法列表中查找
如果在父類方法中一直查到根類還沒(méi)有找到怔接,就會(huì)進(jìn)入消息轉(zhuǎn)發(fā)流程
12搪泳、***?什么是緩存查找,如何進(jìn)行緩存查找扼脐,什么是哈希查找?
在消息傳遞的過(guò)程中用于快速查找方法執(zhí)行函數(shù)森书,如果有一個(gè)緩存就不用到它的方法列表中去查找,可以提高消息傳遞效率
緩存查找就是根據(jù)選擇器因子到緩存哈希表中(cache_t)射出bucket_t在數(shù)組中的位置 找到bucket_t位置提取出imp指針?lè)祷亟o調(diào)用方
通過(guò)我們給定的一個(gè)值經(jīng)過(guò)哈希函數(shù)的算法 算出的這個(gè)值就是bucket_t在數(shù)組中的索引位置
11谎势、在當(dāng)前類中如果查找方法
對(duì)于沒(méi)有排序好的列表凛膏,采用“遍歷方法”查找執(zhí)行函數(shù)
對(duì)于已排序好的列表采用“二分查找”算法查找方法的對(duì)應(yīng)執(zhí)行函數(shù)
12、話一副消息傳遞流程圖
13脏榆、objc_msgSend objc_msgSuperSend有什么區(qū)別
objc_msgSend:
OC的方法調(diào)用:消息機(jī)制,給方法調(diào)用者發(fā)送消息
objc_msgSend會(huì)根據(jù)在信息在對(duì)象isa指針指向的Class中尋找該SEL對(duì)應(yīng)的IMP猖毫,從而完成方法的調(diào)用
任意的OC方法的調(diào)用,比如[obj aMethod]都會(huì)被翻譯成objc_msgSend()
由此進(jìn)入objc_msgSend執(zhí)行
objc_msgSuperSend:
消息轉(zhuǎn)發(fā)會(huì)調(diào)用 objc_msgSendSuper
告訴系統(tǒng)去父類方法列表里面去找但是調(diào)用者主體還是self
super只是一個(gè)編譯器的特殊字符须喂,并不代表父類的一個(gè)實(shí)例化對(duì)象
14吁断、*** 消息的轉(zhuǎn)發(fā)流程是怎樣的
首先 實(shí)例方法的轉(zhuǎn)發(fā)流程會(huì)調(diào)用:resolveInstanceMethod, 類方法的轉(zhuǎn)發(fā)流程會(huì)調(diào)用resolveClassMethod (它是一個(gè)類方法返回值是BOOL 返回值是一個(gè)BOOL告訴系統(tǒng)我們要不要解決當(dāng)前實(shí)例方法的實(shí)現(xiàn) 如果返回YES相當(dāng)于通知系統(tǒng)當(dāng)前消息已經(jīng)處理接觸了消息轉(zhuǎn)發(fā)流程)
如果是NO 系統(tǒng)會(huì)給予我們第二次機(jī)會(huì)處理這條消息會(huì)執(zhí)行 forwardingTargetForselector 這個(gè)方法(這個(gè)方法的返回值是id相當(dāng)于告訴系統(tǒng)這個(gè)方法的調(diào)用應(yīng)該由哪個(gè)對(duì)象來(lái)處理 轉(zhuǎn)發(fā)對(duì)象是誰(shuí) 如果指定了轉(zhuǎn)發(fā)目標(biāo)系統(tǒng)會(huì)把結(jié)果返回給轉(zhuǎn)發(fā)目標(biāo)并結(jié)束消息轉(zhuǎn)發(fā)流程)
如果在沒(méi)有轉(zhuǎn)發(fā)目標(biāo)的情況下,系統(tǒng)會(huì)給我們最后一次會(huì)??調(diào)用methodSingnatureForSelector這個(gè)方法 返回值是NSmethodSignature對(duì)象(這個(gè)對(duì)象是實(shí)際是對(duì)方法選擇器返回類的類型)
如果返回了方法簽名系統(tǒng)會(huì)返回forwardInvocation: 如果forwardInvocation能夠處理則流程結(jié)束 如果NSMethodSignature返回空 則消息無(wú)法處理
15坞生、什么是Method-Swizzling, 一般會(huì)用到哪里
Method-Swizzling 又叫方法混淆仔役,簡(jiǎn)單說(shuō)就是進(jìn)行方法交換 主要用來(lái)修改selector對(duì)應(yīng)的IMP實(shí)現(xiàn)
首先倒入runtime頭文件
通過(guò)class_getClassMethod 獲取方法結(jié)構(gòu)體method
通過(guò)method_exchangeImplementations 進(jìn)行方法交換
實(shí)現(xiàn)方法
新方法替換老方法
頁(yè)面進(jìn)出添加統(tǒng)計(jì)信息
數(shù)組越界 空值導(dǎo)致的崩潰問(wèn)題
是unrecognized selector sent to class會(huì)導(dǎo)致APP崩潰
16、是否使用過(guò)performSelector
在動(dòng)態(tài)添加方法的時(shí)候使用是己,一個(gè)類在編譯時(shí)候沒(méi)有這個(gè)方法又兵,在運(yùn)行時(shí)才產(chǎn)生了這個(gè)方法 在此場(chǎng)景下需要調(diào)用performSelector
class_addMethod(self, @selector(test), testImp, "v@:"); 方法添加
void testImp(void) {NSLog(@"test invoke");}
17、在什么場(chǎng)景下需要?jiǎng)討B(tài)添加方法
?如果一個(gè)類方法非常多,加載類到內(nèi)存的時(shí)候也比較耗費(fèi)資源沛厨,需要給每個(gè)方法生成映射表宙地,可以使用動(dòng)態(tài)給某個(gè)類,添加方法解決
18逆皮、什么是動(dòng)態(tài)方法解析
使用@dynamic關(guān)鍵字進(jìn)行修飾
當(dāng)我們把屬性編修飾為@dynamic時(shí)代表著不需要編譯器為我們生成getter setter的具體實(shí)現(xiàn)
在運(yùn)行時(shí)具體的調(diào)用了getter setter的時(shí)候在去為它添加具體的實(shí)現(xiàn)宅粥,只有動(dòng)態(tài)運(yùn)行時(shí)語(yǔ)言才支持這種功能
19、編譯時(shí)語(yǔ)言與動(dòng)態(tài)運(yùn)行時(shí)語(yǔ)言的區(qū)別电谣, 動(dòng)態(tài)進(jìn)行時(shí)語(yǔ)言有什么優(yōu)點(diǎn)
動(dòng)態(tài)運(yùn)行時(shí)語(yǔ)言將函數(shù)決議由編譯時(shí)推遲到運(yùn)行時(shí) 實(shí)際上就是在運(yùn)行時(shí)為方法添加具體執(zhí)行函數(shù)
編譯時(shí)語(yǔ)言它是在編譯器進(jìn)行函數(shù)決議秽梅,在編譯期間一個(gè)方法名稱所對(duì)應(yīng)的函數(shù)執(zhí)行體是哪個(gè) 在具體運(yùn)行過(guò)程中不能被修改
我們寫(xiě)代碼時(shí)更具靈活性,如我們可以把消息轉(zhuǎn)發(fā)給我們想要的對(duì)象剿牺,或者隨意交換一個(gè)方法的實(shí)現(xiàn)等
20风纠、*** [obj foo]和 objc_msgSend()函數(shù)之間有什么關(guān)系
[obj foo]在編譯器處理后就變成了 objc_msgSend函數(shù)調(diào)用 然后開(kāi)始了runtime的消息傳遞過(guò)程
第一參數(shù)是obj
第二個(gè)參數(shù)是foo選擇器
由于這個(gè)方法沒(méi)有其他桉樹(shù)所以objc_msgSend的餐食只有兩個(gè)
21、runtime如果通過(guò)selector 找到IMP地址 (或者回答消息傳遞機(jī)制)
緩存是否命中
當(dāng)前類方法列表是否命中 (二分查找牢贸,遍歷查找)
逐級(jí)父類方法是否命中 (根據(jù)superClass a, b)
22、能否為編譯后的類中添加實(shí)例變量(注意是編譯后镐捧,還是動(dòng)態(tài)添加)
????1潜索、不可以,因?yàn)榫幾g之前創(chuàng)建的類已經(jīng)完成了變量的布局懂酱, class_ro_t 在編譯后無(wú)法進(jìn)行修改
23竹习、能否為動(dòng)態(tài)添加的類中添加實(shí)例變量????
????1、可以列牺,因?yàn)槲议T在動(dòng)態(tài)添加的類的過(guò)程中只要在它調(diào)用注冊(cè)類隊(duì)之前完成實(shí)例變量的添加就可以實(shí)現(xiàn)
24整陌、***runtime 如何實(shí)現(xiàn) weak 屬性
? ?weak的特點(diǎn):
weak策略表明該屬性定義了一種“非擁有關(guān)系” (nonowning relationship)
為這種屬性設(shè)置新值時(shí),設(shè)置方法既不保留新值瞎领,也不釋放舊值
此特質(zhì)同assign類似;然而在屬性所指的對(duì)象遭到摧毀時(shí)泌辫,屬性值也會(huì)清空(nil out)
? ? runtime 如何實(shí)現(xiàn) weak 變量的自動(dòng)置nil:
runtime 對(duì)注冊(cè)的類會(huì)進(jìn)行布局,對(duì)于weak對(duì)象會(huì)放入一個(gè)hash表中
用weak指向的對(duì)象內(nèi)存地址作為key九默,當(dāng)此對(duì)象的引用計(jì)數(shù)為0的時(shí)候會(huì)dealloc
假如weak指向的對(duì)象內(nèi)存地址是a震放,那么就會(huì)以a為鍵,在這個(gè)weak表中搜索驼修,找到所有以a為鍵的weak對(duì)象從而設(shè)置為 nil
26殿遂、runtime怎么添加屬性、方法等
????1乙各、class_addIvar墨礁、class_addMethod、class_addProperty耳峦、class_addProtocol恩静、class_replaceProperty
27、_objc_msgForward函數(shù)是做什么的蹲坷?直接調(diào)用它將會(huì)發(fā)生什么
_objc_msgForward是IMP類型蜕企,用于消息轉(zhuǎn)發(fā)的咬荷,當(dāng)向一個(gè)對(duì)象發(fā)送一條消息,但它并沒(méi)有實(shí)現(xiàn)的時(shí)候轻掩,_objc_msgForward會(huì)嘗試做消息轉(zhuǎn)發(fā)
28幸乒、*** 消息傳遞與函數(shù)調(diào)用之間的區(qū)別, OC是怎么調(diào)用函數(shù)的
可以隨時(shí)對(duì)一個(gè)對(duì)象傳遞任何消息唇牧,而不需要在編譯的時(shí)候聲明這些方法
消息傳遞:運(yùn)行時(shí)所執(zhí)行的代碼由運(yùn)行時(shí)環(huán)境決定罕扎,消息傳遞可以隨時(shí)對(duì)一個(gè)對(duì)象傳遞任何消息,并且不需要在編譯的時(shí)候去聲明這些方法
函數(shù)調(diào)用:運(yùn)行時(shí)所執(zhí)行的代碼由編譯器決定 函數(shù)如果沒(méi)有聲明就去調(diào)用的話丐重,會(huì)造成編譯失敗腔召,而消息就不會(huì)這樣