Runtime
數(shù)據(jù)結(jié)構(gòu)
- objc_object
- objc_class
- isa指針
- method_t
objc_object id == objc_object
- isa_t
- 關(guān)于isa操作相關(guān)
- 弱引用相關(guān)
- 關(guān)聯(lián)對(duì)象相關(guān)
- 內(nèi)存管理相關(guān)
objc_class class == objc_class 繼承于objc_object
Class superClass
cache_t cache
- 快速查找方法執(zhí)行函數(shù)
- 可增量擴(kuò)展的哈希表結(jié)構(gòu)
- 局部性原理的最佳應(yīng)用
class_data_bits_t bits
- class\_data\_bits\_t主要是對(duì)class_rw_t的封裝
- class\_rw\_t代表了類相關(guān)的讀寫信息审残、對(duì)class\_ro\_t的封裝
class_rw_t
- class_ro_t 代表了類相關(guān)的只讀信息
- protocols
- properties
- methods
class_ro_t
isa指針
- 指針型isa 的值代表Class的地址
- 非指針型isa 的值的部分代表Class的地址
- 產(chǎn)生這兩種的初衷:我們?cè)趯ぶ穼?shí)際上30~40位數(shù)就可以保證我們尋找到所有class的地址,多出來的位可以存儲(chǔ)一些別的內(nèi)容盟猖,來達(dá)到節(jié)省內(nèi)存的目的
- 指向問題:
- 關(guān)于對(duì)象以故,其指向類對(duì)象
- 關(guān)于類對(duì)象累魔,其指向元類對(duì)象
- 如果是實(shí)例方法從類對(duì)象查找鞍陨,如果是類方法從元類對(duì)象查找
method_t
const char * types
type Encodings技術(shù)
數(shù)據(jù)結(jié)構(gòu)總結(jié)
對(duì)象洪燥、類對(duì)象霉祸、元類對(duì)象
類對(duì)象存儲(chǔ)實(shí)例方法列表等信息
元類對(duì)象存儲(chǔ)類方法類表等信息
類對(duì)象和元類對(duì)象都是objc_class數(shù)據(jù)結(jié)構(gòu)轻姿,由于繼承了objc_object數(shù)據(jù)結(jié)構(gòu)所以才有isa指針犁珠,
所以才能實(shí)例對(duì)象找到類對(duì)象訪問它的實(shí)例方法列表等信息,
類對(duì)象通過isa可以找到元類對(duì)象訪問類方法列表等信息
元類對(duì)象的isa指針(包含根元類對(duì)象)都指向根元類對(duì)象
-
根元類對(duì)象的superclass指向的根類對(duì)象
- 當(dāng)在跟類中的類方法沒找到互亮,會(huì)指向根類對(duì)象中去查找它的同名的實(shí)例方法
消息傳遞過程
實(shí)例方法消息傳遞過程
1犁享、 調(diào)用了一個(gè)實(shí)例對(duì)象A對(duì)象的實(shí)例方法
2、 通過A實(shí)例對(duì)象的isa指針找到它的類對(duì)象胳挎,類對(duì)象中遍歷方法列表去查找同名的方法實(shí)現(xiàn)饼疙,找到后Runtime發(fā)送消息調(diào)用
3、 如果沒有找到就會(huì)根據(jù)superclass指針指向的父類對(duì)象,并查找其方法列表窑眯。屏积。。直到根類對(duì)象中的方法列表磅甩,如果找到Runtime發(fā)送消息調(diào)用炊林,如果到根類對(duì)象還沒有找到就會(huì)返回nil
類方的消息傳遞過程
1、 調(diào)用一個(gè)類對(duì)象的類方法卷要。
2渣聚、 通過類對(duì)象的isa指針找到元類對(duì)象,元類對(duì)象中遍歷方法列表查找同名的類方法是心啊僧叉,找到后直接調(diào)用
3奕枝、如果沒有找到就會(huì)通過superclass指針指向父元類對(duì)象,并查找其類方法列表瓶堕。隘道。。直到根元類對(duì)象中的類方法列表郎笆,如果找到直接調(diào)用谭梗,如果沒有找到進(jìn)入到消息轉(zhuǎn)發(fā)
4、根元類對(duì)象如果沒有找到會(huì)superclass會(huì)指向根類對(duì)象宛蚓,并查找同名的實(shí)例方法激捏,如果找到直接調(diào)用,如果沒有找到返回nil
消息傳遞
1凄吏、緩存查找
例: 給定值是SEL 目標(biāo)值是對(duì)應(yīng)bucket_t的IMP远舅。
- 首先給定的函數(shù)選擇器通過一個(gè)函數(shù)來映射出bucket_t在數(shù)組當(dāng)中位置,這個(gè)過程是hash查找
- 哈希查找實(shí)際上竞思,通過給定的一個(gè)值比如說方法的選擇器經(jīng)過哈希函數(shù)的算法酸楚的值實(shí)際上就是給定值在對(duì)應(yīng)數(shù)組當(dāng)中所對(duì)應(yīng)的索引位置表谊。
- f(key) = key & mask
- 通過哈希查 解決查找效率的問題
- 通過哈希查找,找到對(duì)應(yīng)的bucket_t盖喷,就可以拿到IMP
2、當(dāng)前類中查找
對(duì)于已排序好的列表难咕,采用二分查找算法查找方法對(duì)應(yīng)執(zhí)行函數(shù)
對(duì)于沒有排序的列表课梳,采用一般遍歷查找方法對(duì)應(yīng)執(zhí)行函數(shù)
3、父類逐級(jí)查找
消息轉(zhuǎn)發(fā)
Method-Swizzling
Method method1 = class_getInstanceMethod(self, @selector(test));
Method method2 = class_getInstanceMethod(self, @selector(Mytest));
method_exchangeImplementations(method1, method2);
動(dòng)態(tài)添加方法
編譯的時(shí)候沒有這個(gè)方法余佃,運(yùn)行時(shí)有這個(gè)方法需要調(diào)用performSelector
方法,在消息轉(zhuǎn)發(fā)中的第一次處理函數(shù)resolveInstanceMethod
動(dòng)態(tài)添加DoThings:Num:
方法
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"%@",NSStringFromSelector(sel));
if(sel == @selector(DoThings:Num:)){
class_addMethod([self class], sel, (IMP)MyMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void MyMethodIMP(id self,SEL _cmd,NSString * name,NSInteger num){
NSLog(@"調(diào)用了消息轉(zhuǎn)發(fā)的內(nèi)容");
NSLog(@"%@",NSStringFromSelector(_cmd));
NSLog(@"%@",name);
NSLog(@"%ld",(long)num);
}
動(dòng)態(tài)方法解析
@dynamic
- 動(dòng)態(tài)運(yùn)行時(shí)語言將函數(shù)決議推遲到運(yùn)行時(shí)暮刃。
- 當(dāng)我們把一個(gè)屬性標(biāo)示為@dynamic的時(shí)候代表著不需要編譯器在編譯時(shí),為我們生成屬性的setter跟getter方法的具體實(shí)現(xiàn)爆土,而在在具體運(yùn)行時(shí)我們調(diào)用了setter椭懊、geter方法的時(shí)候,再去添加具體的實(shí)現(xiàn)步势。
- 這種功能也只有動(dòng)態(tài)運(yùn)行時(shí)特性的語言支持
- 編譯時(shí)語言在編譯期進(jìn)行函數(shù)決議
面試題
nsobject有父類嗎氧猬?
沒有nsobject是根類
當(dāng)在跟類中的類方法沒找到背犯,但是有同名的實(shí)例方法實(shí)現(xiàn),那么會(huì)不會(huì)崩潰盅抚?會(huì)不會(huì)產(chǎn)生實(shí)際的調(diào)用漠魏?
由于根元類對(duì)象的superclass指針指向了根類對(duì)象,去查找實(shí)例同名的實(shí)例方法妄均,如果找到就會(huì)執(zhí)行同名的實(shí)例方法調(diào)用
下圖中打印的是什么柱锹?
[self class]會(huì)轉(zhuǎn)化成
objc_msgSend(<#id _Nullable self#>, <#SEL _Nonnull op, ...#>)
phone的實(shí)例對(duì)象,[self class]丰包,通過isa找到phone類對(duì)象禁熏,phone類對(duì)象是沒有class的,通過superclass找到父類的邑彪。瞧毙。。直到查找到根類nsobject锌蓄,有calss的實(shí)現(xiàn)升筏,打印出來的自然是phone
[super class]會(huì)轉(zhuǎn)化成
objc_msgSendSuper(<#struct objc_super * _Nonnull super#>, <#SEL _Nonnull op, ...#>)
objc_msgSendSuper實(shí)際的接收者仍然是phone的這個(gè)實(shí)例對(duì)象,objc_msgSendSuper傳遞的含義瘸爽,從phone的這個(gè)實(shí)例對(duì)象的父類開始查找您访, 直到查找到根類nsobject有class的實(shí)現(xiàn),所以打印出來的結(jié)果還是phone
打印結(jié)果都是phone
對(duì)頁面的進(jìn)出添加進(jìn)出信息
使用MethodSwizzling來交換viewDidLoad
和viewWillAppear
剪决,在替換代碼中添加一些埋點(diǎn)操作
[obj foo]和objc_msgSend()函數(shù)之間有什么關(guān)系灵汪?
- 向 obj對(duì)象發(fā)送一條foo的消息,[obj foo]在編譯期處理以后就變成了objc_msgSend(obj,@selector(foo))
- 由于沒有參數(shù)柑潦,所以 objc_msgSend函數(shù)調(diào)用只有2個(gè)參數(shù)
- 然后就開始了runtime的消息傳遞過程
runtime如何通過Selector找到對(duì)應(yīng)的IMP地址的享言?
- 查找當(dāng)前實(shí)例所對(duì)應(yīng)類對(duì)象的緩存,是否有selelctor對(duì)應(yīng)imp實(shí)現(xiàn)如果緩存命中了渗鬼,
我們就把命中的緩存函數(shù)返回給調(diào)用方览露。 - 如果緩存沒有就去當(dāng)前類的方法列表查找selector對(duì)應(yīng)的imp實(shí)現(xiàn)
- 當(dāng)前類如果沒有命中,就根據(jù)當(dāng)前類的superclass逐級(jí)查找父類的方法列表譬胎。差牛。。直到根類對(duì)象堰乔,直到查到selector方法的imp實(shí)現(xiàn)
能否向編譯后的類中增加實(shí)例變量偏化?
class_ro_t ro代表的是readonly
編譯后的類是無法添加實(shí)例變量的。
能否向動(dòng)態(tài)添加的類中增加實(shí)例變量镐侯?
可以的,動(dòng)態(tài)添加的這個(gè)類過程當(dāng)中只要在它調(diào)用注冊(cè)方法之前去完成實(shí)例變量的添加就是可以實(shí)現(xiàn)的
消息傳遞機(jī)制
當(dāng)我們?cè)谙鬟f的過程中如何通過緩存的方法查找侦讨?
isa指針的含義?
- 非指針類型 isa值的部分代表calss地址
- 指針類型isa 代表class地址