这篇文章主要是从源码的角度分析 class_getInstanceMethod
方法和class_getClassMethod
方法的实现, isKindOfClass
和 isMemberOfClass
方法的实现。
class_getInstanceMethod VS class_getClassMethod
class_getInstanceMethod
首先我们先定义一个Person类
NS_ASSUME_NONNULL_BEGIN
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
NS_ASSUME_NONNULL_END
@implementation LGPerson
- (void)sayHello{
NSLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"LGPerson say : Happy!!!");
}
@end
我们看下定义如下函数
void lgInstanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className); // 元类
// 类对象的methods存放的是实例方法,能找到
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
// 元类对象的methods存放的是类方法,找不到
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
// 类对象的methods存放的是实例方法, 找不到
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
// 元类对象的methods存放的是类方法,能找到
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
// 调用函数
lgInstanceMethod_classToMetaclass(pClass);
NSLog(@"Hello, World!");
}
return 0;
}
我们先来看下输出结果
lgInstanceMethod_classToMetaclass - 0x1000031b0-0x0-0x0-0x100003148
从输出结果我们可知:method1
,method4
有值,method2
,method3
没有值
我们先看下class_getInstanceMethod
的源码,关键代码
// 1
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
// fixme nil cls?
// fixme nil sel?
auto const methods = cls->data()->methods();
for (auto mlists = methods.beginLists(),
end = methods.endLists();
mlists != end;
++mlists)
{
// <rdar://problem/46904873> getMethodNoSuper_nolock is the hottest
// caller of search_method_list, inlining it turns
// getMethodNoSuper_nolock into a frame-less function and eliminates
// any store from this codepath.
method_t *m = search_method_list_inline(*mlists, sel);
if (m) return m;
}
return nil;
}
// 2
static method_t *
getMethod_nolock(Class cls, SEL sel)
{
method_t *m = nil;
runtimeLock.assertLocked();
// fixme nil cls?
// fixme nil sel?
ASSERT(cls->isRealized());
while (cls && ((m = getMethodNoSuper_nolock(cls, sel))) == nil) {
cls = cls->superclass;
}
return m;
}
在 类的结构分析文章中,我们可知,instanceMethod
存在于类对象
的methods
里面。
- 1,
auto const methods = cls->data()->methods();
当前cls
为类对象
从这里我们看出,先从类对象
的method_list
里面进行查找。 - 2,如果在
类对象
里,没有找到,在到父类的类对象
的method_list
进行查找
由此我们可以得出,class_getInstanceMethod
,会首先在类对象
的methods
里面查找该方法,如果没有找到,会在父类(super class)
里面进行查找。
如果传入的类是元类
,元类
的methods
存放的是类的类方法
。
class_getClassMethod
void lgClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
// 0 0
// 在 元类的 method()里面查找,找不到实例方法
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
// 在 元类的 method()里面查找,找不到实例方法
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
// 在 元类的 method()里面查找,能找到类方法
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
// 在 元类的 method()里面查找,能找到类方法
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
lgClassMethod_classToMetaclass(pClass);
}
return 0;
}
输出结果如下:
lgClassMethod_classToMetaclass-0x0-0x0-0x100003148-0x100003148
我们看下源码实现 class_getClassMethod
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
Class getMeta() {
// 如果是 元类,就返回自身,防止 getMeta无限递归
if (isMetaClass()) return (Class)this;
else return this->ISA();// 返回元类对象
}
从源码我们可以看出:
- 1,查找
类对象
的类方法
,就是查找元类
的实例方法,在元类
的methods()
里面查找。元类对象
的methods()
保存的是类方法
。
isKindOfClass VS isMemberOfClass
我们先看下如下函数的输出结果
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; //
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; //
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]]; //
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]]; //
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; //
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; //
BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]; //
BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]]; //
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
输出结果如下
2020-09-16 22:49:45.102047+0800 KCObjc[3562:3404422] re1 :1
re2 :0
re3 :0
re4 :0
2020-09-16 22:49:45.108914+0800 KCObjc[3562:3404422] re5 :1
re6 :1
re7 :1
re8 :1
Program ended with exit code: 0
我们依次来分析这四个函数
1,+ (BOOL)isKindOfClass:(Class)cls
:
其源码实现如下
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- 若
cls
为类对象
,则tcls
为元类对象
,判断是否相等,如果不相等,则tcls
=metaSuperclass
,tcls值为元类,根元类或NSObject,到NSObject
为止,因为NSObject
的superClass
为nil
,tcls
为nil
会终止循环。
我们来分析 ret1和ret3的结果:
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; // 1
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]]; // 2
- 1,首先,NSObject的
元类
是不等于NSObject的类对象
,NSObject的元类
的superClass
为NSObject类对象
,所以,re1 == 1
。 - 2,LGPerson的
元类
不等于 LGPerson 的类对象
,元类
的superclass
还是元类
,根元类
的superclass
为NSObject
,所以不会等于 LGPerson类对象,所以 ,re2 == 0
。
2,+ (BOOL)isMemberOfClass:(Class)cls
我们看下其源码实现
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- 只判断一次,判断 isa 是不是指向 cls
我们来分析 re2和re4的结果
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; // 1
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]]; // 2
-
[NSObject class]
的isa
指向NSObject
的元类
,不等于其类对象,re2 = 0 -
[LGPerson class]
的isa
指向LGPerson
的元类
,不等于其类对象,re3 = 0
3,- (BOOL)isKindOfClass:(Class)cls
其源码实现如下
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- 先判断其
类对象
是否与cls
相等,如果不相等,就依次
比较其类对象的父类
。
我们来分析 re5 和 re7
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; // 1
BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]; // 2
- 1, NSObject的
类对象
==NSObject的类对象
,所以 re5的值为1。 - 2,LGPerson的
类对象
==LGPerson的类对象
,所以re7的值为1。
4,- (BOOL)isMemberOfClass:(Class)cls
其源码实现如下:
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
- 只对其
类对象
比较一次。
我们对剩下的最后两个 re6 和 re8 进行分析
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; \\ 1
BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]]; \\ 2
- 通过对该函数的分析,两者都是相等的,所以 re6 == 1,re8 == 1。
总结:
class_getInstanceMethod
:函数会 在其类对象
的method()
里面查找,类对象
里面存放的实例方法
。
class_getClassMethod
: 函数,无论该类是 类对象
还是元类对象
,都在元类
的method()
里面查找,元类
的method()
里面存放的都是类方法。
isKindOfClass
: 1,其实例方法,会循环
判断其类对象
和类对象的父类
是否和传入类
是否相等。2,其类方法,会 循环
判断其元类
和元类的父类
是否和传入类
是否相等。
isMemberOfClass
: 1,其实例方法,只会判断一次,当前类对象
是否等于传入类
。2,其类方法,只会判断一次,判断传入类
是不是其元类
。
网友评论