问题一
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation LGPerson
- (void)sayHello {}
+ (void)sayHappy {}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
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 - re2 :%hhd - re3 :%hhd - re4 :%hhd - ",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 - re6 :%hhd - re7 :%hhd - re8 :%hhd - ",re5,re6,re7,re8);
}
return 0;
}
打印结果如下:
2020-09-17 13:20:35.074630+0800 KCObjc[50956:958090] re1 :1 - re2 :0 - re3 :0 - re4 :0 -
2020-09-17 13:20:35.075304+0800 KCObjc[50956:958090] re5 :1 - re6 :1 - re7 :1 - re8 :1 -
这道题目唯一的疑问就在于re1,答案就在源码之中:
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__
if (slowpath(!obj)) return NO;
Class cls = obj->getIsa();
if (fastpath(!cls->hasCustomCore())) {
for (Class tcls = cls; tcls; tcls = tcls->superclass) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
可能会有疑问,为什么isKindOfClass不是调用+ (BOOL)isKindOfClass:(Class)cls或者-(BOOL)isKindOfClass:(Class)cls方法呢?在llvm源码中有相关注释:
// This is the table of ObjC "accelerated dispatch" functions. They are a set
// of objc methods that are "seldom overridden" and so the compiler replaces the
// objc_msgSend with a call to one of the dispatch functions. That will check
// whether the method has been overridden, and directly call the Foundation
// implementation if not.
// This table is supposed to be complete. If ones get added in the future, we
// will have to add them to the table.
const char *AppleObjCTrampolineHandler::g_opt_dispatch_names[] = {
"objc_alloc",
"objc_autorelease",
"objc_release",
"objc_retain",
"objc_alloc_init",
"objc_allocWithZone",
"objc_opt_class",
"objc_opt_isKindOfClass",
"objc_opt_new",
"objc_opt_respondsToSelector",
"objc_opt_self",
};
大体的意思是说上面的几个方法几乎不会被重写,编译器会先检查上述表中对应的objc方法是否有被重写,如果没有,编译器会将objc_msgSend来调用objc方法替换成了直接调用上述函数,可能这样子在效率上会有些提升。
问题回到
re1,在上图中,根类NSObject的元类即根元类的superClass指向了根类NSObject,因此在objc_opt_isKindOfClass返回了ture。
问题二
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation LGPerson
- (void)sayHello {}
+ (void)sayHappy {}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
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", "InstanceMethod",method1,method2,method3,method4);
Method method5 = class_getClassMethod(pClass, @selector(sayHello));
Method method6 = class_getClassMethod(metaClass, @selector(sayHello));
Method method7 = class_getClassMethod(pClass, @selector(sayHappy));
Method method8 = class_getClassMethod(metaClass, @selector(sayHappy));
LGLog(@"%s-%p-%p-%p-%p","ClassMethod",method5,method6,method7,method8);
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));
LGLog(@"%s-%p-%p-%p-%p","MethodImplementation", imp1,imp2,imp3,imp4);
}
return 0;
}
输出如下:
InstanceMethod - 0x1000031b0-0x0-0x0-0x100003148
ClassMethod-0x0-0x0-0x100003148-0x100003148
MethodImplementation-0x100001ce0-0x7fff71842580-0x7fff71842580-0x100001d10
解析:
class_getInstanceMethod
sayHello是LGPerson的实例方法,因此method1有值,method2为空sayHappy是LGPerson的类方法,因此method3为空,method4有值class_getInstanceMethod实现如下:
/***********************************************************************
* class_getInstanceMethod. Return the instance method for the
* specified class and selector.
**********************************************************************/
Method class_getInstanceMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
// This deliberately avoids +initialize because it historically did so.
// This implementation is a bit weird because it's the only place that
// wants a Method instead of an IMP.
#warning fixme build and search caches
// Search method lists, try method resolver, etc.
lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);
#warning fixme build and search caches
return _class_getMethod(cls, sel);
}
class_getClassMethod
sayHello是LGPerson的实例方法,因此method5、method6为空sayHappy是LGPerson的类方法,因此method7有值,method8为空,但是打印的输出里面method8是有值的,看一下源码:
/***********************************************************************
* class_getClassMethod. Return the class method for the specified
* class and selector.
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
其实class_getClassMethod最终还是调用了class_getInstanceMethod方法,只不过class_getInstanceMethod方法参数由类对象变成了元类对象,也说明了类方法其实就是元类的实例方法这一观点。
同时还可以发现如果class_getClassMethod参数是元类的话会直接变成调用class_getInstanceMethod方法,而参数还是元类。因此method8其实返回的是LGPerson的类方法sayHappy。
class_getMethodImplementation
sayHello是LGPerson的实例方法,因此imp1有值,imp2为空sayHappy是LGPerson的类方法,因此imp3为空,imp4有值
但是实际的输出中imp2和imp3都是有值的。
再来看一下源码:
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
// Translate forwarding function to C-callable external version
if (!imp) {
return _objc_msgForward;
}
return imp;
}
原来是方法实现没有找到的时候会返回_objc_msgForward,也就是消息转发。