我们知道实例方法存储在类中,类方法存储在元类中
今天要探索的是,类方法是属于类还是属于元类
1.创建一个类,定义一个实例方法和类方法做比较
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation LGPerson
- (void)sayHello{
NSLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"LGPerson say : Happy!!!");
}
@end
main.m中这样定义
void lgObjc_copyMethodList(Class pClass){
unsigned int count = 0;
Method *methods = class_copyMethodList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Method const method = methods[i];
//获取方法名
NSString *key = NSStringFromSelector(method_getName(method));
LGLog(@"Method, name: %@", key);
}
free(methods);
}
void lgInstanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
void lgClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
// 元类 为什么有 sayHappy 类方法 0 1
//
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
void lgIMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
// - (void)sayHello;
// + (void)sayHappy;
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
NSLog(@"%s",__func__);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
// 0x0000000100000000
// LGTeacher *teacher = [LGTeacher alloc];
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
lgObjc_copyMethodList(pClass);
lgInstanceMethod_classToMetaclass(pClass);
lgClassMethod_classToMetaclass(pClass);
NSLog(@"Hello, World!");
}
return 0;
}
2.lgObjc_copyMethodList用于获取类的方法列表
3.lgInstanceMethod_classToMetaclass用于获取类的实例方法
4.lgClassMethod_classToMetaclass用于获取类的类方法
- lgIMP_classToMetaclass用户获取方法的调用
打印后结果如下

这么看未免太乱,依次单个分析
- pClass为LGPerson类
- metaClass为LGPerson元类
- method1为从类中 sayhello相应的方法
- method2为从元类中 sayhello相应的方法
- method3为从类中 sayHappy相应的方法
- method4为从元类中 sayHappy相应的方法
- -(void)sayHello;
- +(void)sayHappy;
- -(void)sayHello{
NSLog(@"LGPerson say : Hello!!!");
} - +(void)sayHappy{
NSLog(@"LGPerson say : Happy!!!");
}
lgObjc_copyMethodList 类的方法列表
Method, name: sayHello
在这个函数中获取的是LGPerson类的方法列表,因为实例方法储存在类中,类方法存储在元类中,所以只打印了sayHello方法。
lgInstanceMethod_classToMetaclass 获取类的实例方法
- 分析之前,需要先了解
class_getInstanceMethod
方法,从return Value 后半句得如果传入的类或其父类不包含具有指定的实例方法,则为NULL
。
- 分析之前,需要先了解

看打印
lgInstanceMethod_classToMetaclass - 0x1000081e8--0x0--0x0--0x100008180
-
method1
:0x1000081e8- 从
类
中查找是否有sayHello
实例方法,有返回查找的实例方法
- 从
-
method2
:0x0- 从
元类
中查找是否有sayHello
实例方法,没有找到,则从元类的父类根元类
中查找,没有找到,继续从根元类的父类NSObject
中查找,没有找到,从NSObject父类nil
中查找,也没找到。返回NULL
。所以地址为0x0。
- 从
-
method3
:0x0- 从
类
中查找是否有sayHappy
实例方法,没有找到,则从类的父类根类(NSObject)
中查找,没有找到。从NSObject父类nil
中查找,也没找到。返回NULL
。所以地址为0x0。
- 从
-
method4
:0x100008180- 从
元类
中查找是否有sayHappy
实例方法,找到了sayHappy的实例方法,返回方法的地址。
- 从
lgClassMethod_classToMetaclass 获取类的类方法
- 先了解
class_getClassMethod
方法,如果传入的类或者类的父类中没有找到指定的类方法,则返回NULL
。
找到该方法的底层源码,可以看到class_getClassMethod方法, 调用了class_getInstanceMethod(cls->getMeta(), sel),可以得出,获取类的类方法,就是获取元类的实例方法
。需要注意在getMeta()
中,会先判断cls,如果是元类会直接返回this。其目的是为了防止元类
的无限递归查找。
- 先了解
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
-------------------------
// NOT identical to this->ISA when this is a metaclass
// 当这是一个元类时,返回。否则继续查找。
Class getMeta() {
if (isMetaClassMaybeUnrealized()) return (Class)this;
else return this->ISA()
}
图示如下:

lgClassMethod_classToMetaclass - 0x0--0x0--0x100008180--0x100008180
了解底层逻辑后,分析起来就得心应手了
-
method1
:0x0- 首先判断
类
是否是元类
,否,返回元类
,然后在元类中查找sayHello实例方法。按照元类--->根源类 ---> 根类--->nil
,都没有找到,返回NULL。
- 首先判断
-
method2
:0x0- 首先判断
元类
是否是元类
,是,直接返回元类
,然后在元类中查找sayHello实例方法。按照元类--->根源类 ---> 根类--->nil
,都没有找到,返回NULL。
- 首先判断
-
method3
: 0x100008180- 首先判断
类
是否是元类
,否,返回元类
,然后在元类中查找sayHappy实例方法。按照元类--->根源类 ---> 根类--->nil
,元类中
找到了,返回实例方法的地址。
- 首先判断
-
method4
: 0x100008180- 首先判断
类
是否是元类
,是,直接返回元类
,然后在元类中查找sayHappy实例方法。按照元类--->根源类 ---> 根类--->nil
,元类中
找到了,返回实例方法的地址。
现在出现疑问了,我们会发现method4
,它既是元类的实例方法,又是元类的类方法。为什么元类中有sayHappy类方法?这个是因为我们上边说的防止元类的无限递归。在class_getClassMethod
判断是直接返回元类,所以此时查找类方法时,然后按照class_getInstanceMethod
就变成了查找元类中的实例方法
。所以会查找到。
- 首先判断
lgIMP_classToMetaclass 返回方法的具体实现
- 先了解
class_getMethodImplementation
方法,得出class_getMethodImplementation()可能比method_getImplementation(class_getInstanceMethod(cls, name))更快。返回的函数指针可能是运行时内部的函数,而不是实际的方法实现。例如,如果类的实例不响应选择器,则返回的函数指针将是运行时消息转发机制的一部分
。
- 先了解
底层代码
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
lockdebug_assert_no_locks_locked_except({ &loadMethodLock });
imp = lookUpImpOrNilTryCache(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
// Translate forwarding function to C-callable external version
if (!imp) {
return _objc_msgForward;
}
return imp;
}
打印结果分析验证
gIMP_classToMetaclass - 0x100003d20--0x7fff2049c5c0--0x7fff2049c5c0--0x100003d50
- imp1: 0x100003d20
-
类
中可以查找到sayHello
的具体实现,所以返回imp函数指针的地址
-
- imp2: 0x7fff2049c5c0
-
元类
中没有sayHello
的具体实现,因为实例方法存储在类中,所以进行了消息转发
-
- imp3: 0x7fff2049c5c0
-
类
中没有sayHappy
的具体实现,因为类方法存储在元类中,所以进行了消息转发
-
- imp4: 0x100003d50
-
元类
中有sayHappy
的具体实现,因为类方法存储在元类中,所以返回imp函数指针的地址
-
总结
-
class_getInstanceMethod
:获取实例方法
,如果传入的类或其父类带有指定选择器的实例方法,返回方法地址,否则返回NULL。 -
class_getClassMethod
:获取类方法
,如果传入的类或其父类带有指定选择器的类方法,返回方法地址,否则返回NULL。 -
class_getMethodImplementation
:获取方法的具体实现
,如果传入的类有指定方法的实现,返回imp函数指针地址,否则进行消息转发。
网友评论