事情的始末是這樣的腹殿,同學(xué)想驗證一下resolveClassMethod是否執(zhí)行(resolveClassMethod是一個對象調(diào)用一個不存在類方法時验游,會執(zhí)行此方法轧坎,不懂的要惡補一下了矾瘾,可以看我這篇文章:Objective-C消息轉(zhuǎn)發(fā)),然后發(fā)來了如下代碼:
[NSObject performSelector:@selector(hehe)];
當(dāng)時看完之后產(chǎn)生了疑惑丹鸿,performSelector是一個實例方法,NSObject是一個類棚品,難道編譯不會報錯嗎靠欢?后來親測發(fā)現(xiàn)確實不會報錯廊敌。
然后開始了我們今天的故事:
我們都知道下面這樣寫一定會出錯
@interface TestObject : NSObject
@end
@implementation TestObject
- (void)method1{
[TestObject method2];//這樣調(diào)用一定會編譯錯誤
}
- (void)method2{}
然而這樣寫卻不會報錯
@interface TestObject : NSObject
@end
@implementation TestObject
- (void)method{
[NSObject performSelector:@selector(hehe)];
}
甚至于這樣寫也不會報錯
創(chuàng)建一個NSObject的類目
@interface NSObject (hehe)
+(void)run;
@end
@implementation NSObject (hehe)
-(void)run{
NSLog(@"run.....");
}
@end
然后調(diào)用
[NSObject run];
這是為什么哪?
看一張關(guān)系圖
(此圖來源自網(wǎng)絡(luò))
假設(shè)A類繼承自B類门怪,B類繼承自NSObject
A便是途中的Subclass(class),B便是圖中的Superclass(class),NSObject便是Root class(class);
A *a = [A new];
其實A和a一樣骡澈,也是對象,A稱為類對象掷空,a稱為實例對象
每一個類對象都有一個isa指針
Class isa OBJC_ISA_AVAILABILITY;
這個isa指針的指向就是該類對象的元類肋殴,每一個類都是它的元類的對象,元類是對類對象的描述坦弟,就像類是普通實例對象的描述一樣护锤。
每一個類里面聲明的類方法,其本質(zhì)就是把該類方法放到元類的方法列表上面酿傍,所以類在調(diào)用類方法時烙懦,可以想象成是元類的對象在調(diào)用一個實例方法。
A的父類是B赤炒,A的元類的父類是B元類的父類修陡,B的父類是NSObject,NSObject的父類是nil,B元類的父類是NSObject的元類;特別注意的一點可霎,NSObject的元類的父類是NSObject,NSObject的isa指針又指向NSObject的元類魄鸦,所以在NSObject里面的所有方法,NSObject的元類也都擁有癣朗,1拾因、所以用NSObject 調(diào)用任意NSObject里面的實例方法都是可以成功的,2旷余、這也就解釋了上面的聲明里面是+(void)run;類方法绢记,實現(xiàn)里面是-(void)run{ NSLog(@"run.....");}實例方法,調(diào)用卻不會崩潰正卧。
類和元類是一個閉環(huán)蠢熄,實例指向類,類指向元類炉旷,元類指向跟元類签孔,跟元類指向自身,根元類的父類是NSObject
元類是 Class 對象的類窘行。每個類(Class)都有自己獨一無二的元類(每個類都有自己第一無二的方法列表)饥追。這意味著所有的類對象都不同。
NSObject里面的所有實力方法罐盔,任意類都可以通過類方法調(diào)用但绕。
所有的meta-class使用基類的meta-class作為自己的基類,對于頂層基類的meta-class也是一樣,只是它指向自己而已