先看一个经常出现的面试题:
@interface NSObject (Test)
- (void)test;
@end
@implementation NSObject (Test)
- (void)test {
NSLog(@"test")'
}
@end
[NSObject test];
[[[NSObject alloc] init] test];
问题:分别输出什么?
答案:两个都输出的是"test";
之前对使用类对象直接调用方法并且成功输出充满了疑惑,为什么明明一个实例方法-(void)test
;确可以使用类对象NSObject直接调用,其实这里就涉及到元类的相关知识;
先看一下网上经常出现的一张图:

配合图片理解一下元类的定义:
元类是类对象所属的类,就像类对象是实例对象所属的类一样,反过来说类对象是元类的实例对象,实例方法是存储在类对象里的,类方法是存储在元类对象里面。
这里还应该注意一下根类对象的父类是nil,根类对象的元类是Meta Class,元类的元类是他自身,元类的父类是当前的根类对象。
我们通过代码验证一下:
Class class = NSClassFromString(@"NSObject");
Class metaClass = objc_getMetaClass("NSObject");
Class superClass = class_getSuperclass(class);
Class metaSuperClass = class_getSuperclass(metaClass);
输出结果:

由此可见,验证正确。
到此,已经可以看出:
当[NSObject test];
时,首先会从NSObject的元类中查找+(void)test;
方法,在元类中没有找到,接着从元类的父类中查找,在父类中找到,调用成功。
验证一下:
Class class = NSClassFromString(@"NSObject");
Class metaClass = objc_getMetaClass("NSObject");
Class superClass = class_getSuperclass(class);
Class metaSuperClass = class_getSuperclass(metaClass);
Method method = class_getClassMethod(metaSuperClass, @selector(test));
NSLog(@"%@",metaClass);
IMP imp = method_getImplementation(method);
(IMP) imp = 0x0000000106eca4d0 (YDemo`-[NSObject(Test) test] at NSObject+Test.m:13)
这里我获取方法是获取的类方法class_getClassMethod
,但是我发现使用class_getInstanceMethod
也获取到test的实例方法,这里有点不明白,我的分类中明明只实现了-(void)test
,为什么会同时存在类方法与实例方法,求大佬指点下。
更新
上面最后遗留的问题,为什么通过class_getClassMethod
和class_getInstanceMethod
都可以获取到test方法,这里看一下
class_getClassMethod
的源码:
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
其实class_getClassMethod
是获取元类的实例方法,所以这里的实例方法[NSObject test];
就会变成是class_getInstanceMethod(NSObject->getMeta(), test);
到了元类里面其实就是实例方法了。
从这里可以看出在元类中的类方法都是以实例方法存储的。
网友评论