问题
已知 WLWPerson : NSObject
, 以下打印是什么
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; //
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; //
BOOL re3 = [(id)[WLWPerson class] isKindOfClass:[WLWPerson class]]; //
BOOL re4 = [(id)[WLWPerson class] isMemberOfClass:[WLWPerson 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)[WLWPerson alloc] isKindOfClass:[WLWPerson class]]; //
BOOL re8 = [(id)[WLWPerson alloc] isMemberOfClass:[WLWPerson class]]; //
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
复制代码
打印结果
2021-07-06 15:49:48.316771+0800 KCObjcBuild[16324:1396474]
re1 :1
re2 :0
re3 :0
re4 :0
2021-07-06 15:49:48.319309+0800 KCObjcBuild[16324:1396474]
re5 :1
re6 :1
re7 :1
re8 :1
复制代码
继承与ISA流程图(重点)
isKindOfClass分析
具体调用的函数
要想搞清楚这个问题,我们首先要知道isKindOfClass
的底层调用了什么函数,这个可以通过汇编
查看具体的调用函数,不要直接在源码里去查找,因为有可能在编译阶段llvm
对其做了中间层的处理,就像调用+ alloc
,实际先调用的是objc_alloc
可以看出isKindOfClass
实际调用的是 objc_opt_isKindOfClass
- 查看
llvm
源码发现,一个优先调用的表,the table of ObjC "accelerated dispatch" functions
- 因为这些方法很少会被重写,因此编译器会替换调用
objc_msgSend
函数这将检查该方法是否被覆盖,并直接调用该函数
判断的逻辑
- 再搞清楚具体的判断逻辑是什么,在源码中找到该函数
objc_opt_isKindOfClass
- 注释也有表明,调用
isKindOfClass
会走这里 - 肯定是
__OBJC2__
,最近的版本都会走这里 Class cls = obj->getIsa()
- obj 是 实例对象,如:
[NSObject alloc]
则返回该类
- obj 是
类
,例如:[NSObjct Class]
, 则返回元类
- obj 是 实例对象,如:
fastpath(!cls->hasCustomCore())
=true
是否有默认的 new/self/class/respondsToSelector/isKindOf
,我们都知道继承NSObject
肯定有这些方法,所以是两次取反正好是true
Class tcls = cls; tcls; tcls = tcls->getSuperclass()
, 拿到父类
tcls == otherClass
拿self.class
和self.superClaee
与其比较, 直到nil
,有相同则是true
,没有反之((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass)
, 如果不走上面的逻辑,则会通过objc_msgSend
发送消息,调用isKindOfClass
, 但目前的版本,不会走到这里
总结
- 如果是实例对象 则用自己的类或自己的父类比较
- 如果是类,则用元类或元类的父类比较
解析
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
复制代码
[NSObject class]
是根类
, 所以是拿根元类或根元类的父类
与根类
比较根元类的父类
是根类
,所以rel1 = 1
BOOL re3 = [(id)[WLWPerson class] isKindOfClass:[WLWPerson class]];
复制代码
[WLWPerson class]
是WLWPerson 类
,所以是拿MetaWLWPerson
或MetaWLWPerson
的父类 与WLWPerson 类
比较- 首先
MetaWLWPerson != WLWPerson
MetaWLWPerson 的父类
是MetaNSObject
,MetaNSObject != WLWPerson
MetaNSObject
的父类是NSObject
,NSObject != WLWPerson
, 所以re3 = 0
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
复制代码
[NSObject alloc]
是实例对象,所以是拿NSObject
或NSObject
的父类与NSObject
比较- 显然
NSObject = NSObject
, 所以re5 = 1
BOOL re7 = [(id)[WLWPerson alloc] isKindOfClass:[WLWPerson class]];
复制代码
[WLWPerson alloc]
是实例对象,所以是拿WLWPerson
或WLWPerson
的父类与NSObject
比较WLWPerson
的父类 是NSObject
,NSObject = NSObject
, 所以re7 = 1
isMemberOfClass分析
底层调用的方法
- 如果是类方法就是拿
self -> ISA()
也就是元类
与其比较 - 实例方法
[self class]
也就对象的类与其比较
解析
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
复制代码
[NSObject class]
调用类方法,所以是拿 根元类MeataRootNSObject
与 根类NSObject
比较- 显然
MeataRootNSObject != NSObject
, 所以re2 = 0
BOOL re4 = [(id)[WLWPerson class] isMemberOfClass:[WLWPerson class]];
复制代码
[WLWPerson class]
调用类方法, 所以MetaWLWPerson
与WLWPerson
比较MetaWLWPerson != WLWPerson
, 所以re4 = 0
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re8 = [(id)[WLWPerson alloc] isMemberOfClass:[WLWPerson class]];
复制代码
- 都是调用实例方法,所以是拿 对象的类与 值比较,显然都是同一个类
- 所以
re6 = re8 = 1
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END