先上一張經(jīng)典圖:
一悠咱、通過上圖我們可以得到幾個(gè)關(guān)鍵詞:(Instance of Subclass)實(shí)例對(duì)象、(class)類對(duì)象、(meta)元類埠胖。
并有如下結(jié)論:
1、實(shí)例對(duì)象的isa指針指向其所屬的class類淳玩,這個(gè)類本質(zhì)也是一個(gè)對(duì)象直撤,即類對(duì)象。
2蜕着、類對(duì)象的isa指針指向meta元類谋竖。
3、繼承樹上所有元類的isa指針都指向根類(一般是NSObject)的元類承匣。
4蓖乘、根類的元類的isa指針指向自己。
5韧骗、根類的元類的父類是根類本身嘉抒,形成了一個(gè)閉環(huán)
6、根類的父類是nil袍暴。
二些侍、
先定義幾個(gè)變量
Son *s = [Son new];
Class metaSon = objc_getMetaClass("Son");
上面代碼中s是實(shí)例對(duì)象隶症、Son是類(類對(duì)象)、metaSon是元類岗宣。
我們先通過category給NSObject增加一個(gè)實(shí)例方法- (void)eat和一個(gè)類方法+ (void)eat蚂会。
值得注意的是
- (void)eat是添加在類對(duì)象NSObject的方法列表中的,
+ (void)eat則是添加在NSObject的元類的方法列表中的狈定。
通過category添加的方法會(huì)在Runtime時(shí)動(dòng)態(tài)添加在類的方法列表的最前端颂龙,消息傳遞時(shí)會(huì)先執(zhí)行category添加的方法然后return,造成添加了相同名字的方法會(huì)覆蓋掉原來的方法的假象纽什,其實(shí)原來的方法還存在于方法列表中措嵌,只是位置靠后,被靠前的同名方法攔截了芦缰。
@interface NSObject (LiLi)
+ (void)eat;
- (void)eat;
@end
@implementation NSObject (LiLi)
- (void)eat
{
NSLog(@"lili eat shit");
}
+ (void)eat
{
NSLog(@"lili eat shit plus");
}
@end
Father類繼承于NSObject企巢,并重寫了- (void)eat方法;
@interface Father : NSObject
@end
@implementation Father
- (void)eat
{
NSLog(@"father eat food");
}
@end
Son類繼承于Father让蕾。
@interface Son : Father
@end
@implementation Son
@end
下面分析如下代碼會(huì)輸出什么
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Son *s = [Son new];
[s eat];
}
@end
消息傳遞通過Runtime實(shí)現(xiàn)浪规,[s eat]在運(yùn)行時(shí)會(huì)轉(zhuǎn)換為objc_msgSend(s,eat),執(zhí)行流程為:
1探孝、通過s的isa指針找到其所屬的類即Son笋婿,然后在Son的方法列表中查找eat方法,如果找到eat則執(zhí)行顿颅,否則進(jìn)行第二步缸濒。
2、通過Son的super_class指針找到其父類Father粱腻,然后在Father的方法列表中查找eat方法庇配,如果找到eat則執(zhí)行,否則沿著其繼承樹一直向上查找绍些。
3捞慌、如果一直查找到根類(一般為NSObject)還是沒有查找到eat方法,則進(jìn)入消息轉(zhuǎn)發(fā)流程柬批,后面會(huì)介紹消息轉(zhuǎn)發(fā)的三次機(jī)會(huì)啸澡。
在上例中NSObject通過category添加了實(shí)例方法- (void)eat,F(xiàn)ather類中重寫了- (void)eat方法氮帐,而Son類中無任何實(shí)現(xiàn)锻霎。
所以根據(jù)上述消息傳遞過程,會(huì)打印出Father類里重寫的eat方法:
father eat food
如果Son類中重寫了eat方法則會(huì)執(zhí)行Son類里的eat方法揪漩,如果Son類和Father類都沒有重寫eat方法,則執(zhí)行NSObject (LiLi)里的eat方法吏口。
再看如下代碼奄容,同樣的分類增加兩個(gè)方法,其中+ (void)eat只有聲明冰更,沒有實(shí)現(xiàn)。
@interface NSObject (LiLi)
+ (void)eat;
- (void)eat;
@end
@implementation NSObject (LiLi)
- (void)eat
{
NSLog(@"lili eat shit");
}
@end
Father類繼承于NSObject
@interface Father : NSObject
@end
@implementation Father
@end
Son類繼承于Father昂勒。
@interface Son : Father
@end
@implementation Son
@end
下面分析如下代碼會(huì)輸出什么蜀细?
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Class metaSon = objc_getMetaClass("Son");
[Son eat];
}
@end
我們執(zhí)行的是+ (void)eat類方法,消息傳遞時(shí)卻執(zhí)行了實(shí)例方法- (void)eat戈盈,故打印如下:
lili eat shit
分析如下:
類方法+ (void)eat的消息傳遞流程類似于上述流程奠衔,特殊的地方在于NSObject的元類的父類是NSObject本身,我們前面第5點(diǎn)結(jié)論說過NSObject和NSObject的元類形成了一個(gè)閉環(huán)塘娶,所以NSObject的類方法在此處可以傳遞到NSObject的實(shí)例方法上归斤。
Runtime時(shí) [Son eat]在運(yùn)行時(shí)會(huì)轉(zhuǎn)換為objc_msgSend(Son,eat),搜索的是繼承樹上所有元類的方法列表:
1刁岸、通過Son的isa指針找到其所屬的元類即metaSon脏里,然后在metaSon的方法列表中查找+ (void)eat方法,如果找到+ (void)eat則執(zhí)行虹曙,否則進(jìn)行第二步迫横。
2、通過metaSon的super_class指針找到其父類即Father的元類酝碳,然后在Father的元類方法列表中查找+ (void)eat方法矾踱,如果找到+ (void)eat則執(zhí)行,否則沿著其繼承樹一直向上查找疏哗。
3呛讲、如果一直查找到根類(NSObject的元類)還是沒有查找到+(void)eat方法,則會(huì)去NSObject的元類的父類(即NSObject本身)中查找實(shí)例方法- (void)eat沃斤,此處類方法傳遞為實(shí)例方法圣蝎,還是比較特殊的。
以上衡瓶。