关于isa
的走位和类
的继承关系
isKindOfClass
& isMemberOfClass
// 面试题 1
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[MuPerson class] isKindOfClass:[MuPerson class]];
BOOL re4 = [(id)[MuPerson class] isMemberOfClass:[MuPerson class]];
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[MuPerson alloc] isKindOfClass:[MuPerson class]];
BOOL re8 = [(id)[MuPerson alloc] isMemberOfClass:[MuPerson class]];
isKindOfClass
先看源码实现
// 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);
}
-
isKindOfClass
底层源码实现objc_opt_isKindOfClass
,这里有个细节llvm
在编译阶段做了处理,使得isKindOfClass
走objc_opt_isKindOfClass
。方法内部实现:拿当前的传入对象的isa
,呢么分为2
种情况:- 1 、传入的
obj
是类,类的isa
就是类的元类
,如果存在,就和当前的类的对比。如果不相等,就拿类的元类
的父类(根元类
),甚至于类的元类
的父类的父类(NSObject
),直到nil
。tip
:这种情况的对比链就是类
和元类
的继承链。 - 2、传入的
obj
是类的对象,类对象的isa
就是类本身,如果存在,就和当前的类对比。如果不相等,就拿类的父类,甚至于类的父类的父类,直到NSObject
、nil
。tip
:这种情况的对比链就是类
和类
的继承链。所以是一定成立的。
- 1 、传入的
isMemberOfClass
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
isMemberOfClass
类方法实现:拿当前的类的元类
,和当前的类对比。tip
:一定不成立,类的元类
和当前的类肯定不相等。isMemberOfClass
实例化方法实现:拿当前的对象的类,如果存在,就和当前的类对比。tip
:一定成立,当前的对象的类肯定和当前的类是相等的。
isKindOfClass
&& isMemberOfClass
- 从源码的实现上,我们可以看到,
isMemberOfClass
其实只判断了isKindOfClass
判断链的第一步,就返回了。
面试题1分析
- 除了1、3需要我们分析,其他的都是干扰项。5和7一定成立,2和4一定不成立,6和8都是一定成立的。
- 分析1,otherClass = NSObject, tcls = {NSObject->ISA() 也就是 根元类 ->NSObject -> nil},NSObject 先和 根元类 对比,再和 NSObject 对比,最终返回YES
- 分析3,otherClass = MuPerson, tcls = {MuPerson->ISA() 也就是 元类 -> 根元类 -> NSObject -> nil},MuPerson 依次和 元类、根元类、NSObject、nil对比,最终返回NO
方法的追踪
// 面试题 2
@interface MuPerson : NSObject
- (void)funcHello;
+ (void)funcWorld;
@end
- (void)funcHello{
NSLog(@"hello");
}
+ (void)funcWorld{
NSLog(@"world");
}
MuPerson *person = [MuPerson alloc];
Class pClass = object_getClass(person);
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(funcHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(funcWorld));
Method method3 = class_getInstanceMethod(pClass, @selector(funcHello));
Method method4 = class_getInstanceMethod(metaClass, @selector(funcWorld));
Method method5 = class_getClassMethod(pClass, @selector(funcHello));
Method method6 = class_getClassMethod(metaClass, @selector(funcHello));
Method method7 = class_getClassMethod(pClass, @selector(funcWorld));
Method method8 = class_getClassMethod(metaClass, @selector(funcWorld));
IMP imp1 = class_getMethodImplementation(pClass, @selector(funcHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(funcHello));
IMP imp3 = class_getMethodImplementation(pClass, @selector(funcWorld));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(funcWorld));
class_getInstanceMethod
- 从官方文档上可以看到,
class_getInstanceMethod
是为了查找当前类
乃至这个类
的父类
们有没有这个实例化方法
。
class_getClassMethod
- 从官方文档上可以看到,
class_getClassMethod
是为了查找当前类
乃至这个类
的父类
们有没有这个类方法
。
这里可能大家会觉得和class_getInstanceMethod
一样,其实不然,看下源码实现:
//获取类方法
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
判断是否是元类,是元类就直接返回,反之,继续找isa指向
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
- 可以得出
class_getClassMethod
的实现是获取类
的类方法
,本质为是获取元类
的实例方法
,最终还是会走到class_getInstanceMethod
,但是在这里有一个细节:在getMeta
源码中,如果判断出cls
是元类,那么就不会再继续往下递归查找,会直接返回this
当前的元类
,其目的是为了防止在查找到根元类
的时候,根元类的isa
还是指向了自身的无限递归
查找
class_getMethodImplementation
- 从官方文档上可以看到,
class_getMethodImplementation
是为了查找当前类
的方法
的IMP
指针。如果在当前类找到了就返回IMP
指针,没有找到就返回消息转发指针
。
面试题2解析
class_getInstanceMethod
的1、2、3、4- 1、
funcHello
实例化方法,MuPerson
的bits
存的有 - 2、
funcHello
实例化方法,MuPerson
的元类的bits
中没有 - 3、
funcWorld
类方法,MuPerson
的bits
中没有 - 4、
funcWorld
类方法,MuPerson
的元类的bits
中有
- 1、
class_getClassMethod
的5、6、7、8- 1、pClass
MuPerson
不是元类,向下查找MuPerson
的元类,funcHello
实例化方法,MuPerson
的元类的bits
中没有 - 2、 metaClass为
MuPerson
的元类,则不向下查找。funcHello
实例化方法,MuPerson
的元类的bits
中没有 - 3、pClass
MuPerson
不是元类,向下查找MuPerson
的元类,funcWorld
类方法,MuPerson
的元类的bits
中有 - 4、 metaClass为
MuPerson
的元类,则不向下查找。funcWorld
类方法,MuPerson
的元类的bits
中有
- 1、pClass
class_getMethodImplementation
的1、2、3、4- 1、
funcHello
实例化方法,MuPerson
的bits
存的有,则返回IMP
- 2、
funcHello
实例化方法,MuPerson
的元类的bits
中没有,则返回消息转发指针
。 - 3、
funcWorld
类方法,MuPerson
的bits
中没有,则返回消息转发指针
。 - 4、
funcWorld
类方法,MuPerson
的元类的bits
中有,则返回IMP
- 1、