事情的始末是這樣的,同學想驗證一下resolveClassMethod是否執行(resolveClassMethod是一個對象調用一個不存在類方法時,會執行此方法,不懂的要惡補一下了,可以看我這篇文章:Objective-C消息轉發),然后發來了如下代碼:
[NSObject performSelector:@selector(hehe)];
當時看完之后產生了疑惑,performSelector是一個實例方法,NSObject是一個類,難道編譯不會報錯嗎?后來親測發現確實不會報錯。
然后開始了我們今天的故事:
我們都知道下面這樣寫一定會出錯
@interface TestObject : NSObject
@end
@implementation TestObject
- (void)method1{
[TestObject method2];//這樣調用一定會編譯錯誤
}
- (void)method2{}
然而這樣寫卻不會報錯
@interface TestObject : NSObject
@end
@implementation TestObject
- (void)method{
[NSObject performSelector:@selector(hehe)];
}
甚至于這樣寫也不會報錯
創建一個NSObject的類目
@interface NSObject (hehe)
+(void)run;
@end
@implementation NSObject (hehe)
-(void)run{
NSLog(@"run.....");
}
@end
然后調用
[NSObject run];
這是為什么哪?
看一張關系圖
(此圖來源自網絡)
假設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指針的指向就是該類對象的元類,每一個類都是它的元類的對象,元類是對類對象的描述,就像類是普通實例對象的描述一樣。
每一個類里面聲明的類方法,其本質就是把該類方法放到元類的方法列表上面,所以類在調用類方法時,可以想象成是元類的對象在調用一個實例方法。
A的父類是B,A的元類的父類是B元類的父類,B的父類是NSObject,NSObject的父類是nil,B元類的父類是NSObject的元類;特別注意的一點,NSObject的元類的父類是NSObject,NSObject的isa指針又指向NSObject的元類,所以在NSObject里面的所有方法,NSObject的元類也都擁有,1、所以用NSObject 調用任意NSObject里面的實例方法都是可以成功的,2、這也就解釋了上面的聲明里面是+(void)run;類方法,實現里面是-(void)run{ NSLog(@"run.....");}實例方法,調用卻不會崩潰。
類和元類是一個閉環,實例指向類,類指向元類,元類指向跟元類,跟元類指向自身,根元類的父類是NSObject
元類是 Class 對象的類。每個類(Class)都有自己獨一無二的元類(每個類都有自己第一無二的方法列表)。這意味著所有的類對象都不同。
NSObject里面的所有實力方法,任意類都可以通過類方法調用。
所有的meta-class使用基類的meta-class作為自己的基類,對于頂層基類的meta-class也是一樣,只是它指向自己而已