事情的始末是这样的,同学想验证一下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也是一样,只是它指向自己而已
网友评论