1.什么是Runtime?
OC是一門動(dòng)態(tài)語言,它將很多操作推遲到運(yùn)行時(shí)再進(jìn)行,所以光有編譯器是不夠,還需要一個(gè)運(yùn)行時(shí)系統(tǒng),而Runtime就是OC的運(yùn)行時(shí)機(jī)制,它是一套由C、C++、匯編編寫的API,封裝了很多動(dòng)態(tài)性相關(guān)的函數(shù)。在我們平時(shí)編寫的OC代碼,在程序運(yùn)行過程中,其實(shí)最終都轉(zhuǎn)成了Runtime的C語言方法。
2.方法的本質(zhì)是什么?runtime的消息機(jī)制
方法的本質(zhì)就是發(fā)送消息。
發(fā)送消息的詳細(xì)流程:方法調(diào)用轉(zhuǎn)化為 objc_msgSend(id self, SEL _cmd, ...)
- 首先會(huì)進(jìn)行匯編快速查找:
objc_object(對(duì)象) --> isa_t(isa) -->objc_class(類對(duì)象) --> cache_t(cache) --> buckets<struct bucket_t *>,bucket_t中存儲(chǔ)了_sel和_imp;cache_t使用了哈希算法,解決哈希沖突使用了開放尋址法,將cache_t結(jié)構(gòu)體中的mask與sel進(jìn)行與運(yùn)算找到對(duì)應(yīng)的imp - 慢速查找:
objc_class(類對(duì)象) --> objc_method_list(methodLists)-->objc_method遍歷查找,遞歸自己和父類查找方法lookUpImpOrForward - 查找不到消息,進(jìn)行動(dòng)態(tài)方法解析
resolveInstanceMethod,方法內(nèi)部會(huì)檢查對(duì)象是否實(shí)現(xiàn)了+(BOOL)resolveInstanceMethod:(SEL)sel,因此可在此方法中為sel添加一個(gè)imp,此方法返回YES/NO只影響debug日志 - 消息快速轉(zhuǎn)發(fā):
- (id)forwardingTargetForSelector:(SEL)aSelector提供一個(gè)消息接收者對(duì)象 - 消息慢速轉(zhuǎn)發(fā):消息簽名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector和- (void)forwardInvocation:(NSInvocation *)anInvocation - 若消息最終都沒有被處理會(huì)報(bào)錯(cuò)
unrecoginzed selector send to instance xxx
3.SEL是什么?IMP是什么??jī)烧哂惺裁绰?lián)系?
-
SEL是方法編號(hào),即方法名稱,在dyld加載鏡像時(shí),通過map_images-->map_images_nolock-->_read_images方法加載到內(nèi)存的表中,而IMP是函數(shù)實(shí)現(xiàn)的指針
4.runtime的應(yīng)用場(chǎng)景
- 用于方法交換
MethodSwizzling,改變已存在的selector的實(shí)現(xiàn),例如改變基類的方法實(shí)現(xiàn)app的統(tǒng)一配置,這里一般是在+load方法中,且為了保證方法交換只執(zhí)行一次使用dispatch_once進(jìn)行包裝 - 實(shí)現(xiàn)給分類添加屬性,尤其是系統(tǒng)類,利用
Runtime的關(guān)聯(lián)對(duì)象可為分類添加新的屬性 - 實(shí)現(xiàn)字典轉(zhuǎn)模型如
YYModel,利用Runtime的api獲取屬性列表,賦值等操作 - 獲取類的所有屬性/方法名稱,使用
Runtime的apiclass_copyIvarList、class_copyMethodList - aspect切面編程
5.能否在分類中添加屬性和成員變量?能否向已存在的類添加實(shí)倒變量?
- 分類的結(jié)構(gòu)體中沒有屬性列表,只有方法列表,原則上只能添加方法,不能添加屬性,但是可以借助運(yùn)行時(shí)關(guān)聯(lián)對(duì)象。
- 分類中可以使用
@property, 但是不會(huì)生成setter/getter方法,也不會(huì)生成私有的成員變量,編譯可以通過但引用就會(huì)報(bào)錯(cuò)。 - 編譯后的類已經(jīng)注冊(cè)在
runtime中,類結(jié)構(gòu)體中的objc_ivar_list實(shí)例變量的鏈表和instance_size實(shí)例變量的內(nèi)存大小已經(jīng)確定,同時(shí)runtime還會(huì)調(diào)用class_setIvarLayout、class_setWeakIvarLayout來處理strong、weak引用,所以不能向已存在的類中添加實(shí)例變量。但是運(yùn)行時(shí)使用objc_allocateClassPair創(chuàng)建類,可以調(diào)用class_addIvar函數(shù)添加實(shí)例變量,但必須在調(diào)用objc_registerClassPair前。