面试题
话不多说,通过一个面试题来探究下isKindOfClass
和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);
复制代码
这段代码运行的结果是多少呢
结果
2021-07-09 16:22:06.480627+0800 KCObjcBuild[74708:6582366] re1 :1
re2 :0
re3 :0
re4 :0
2021-07-09 16:22:06.481524+0800 KCObjcBuild[74708:6582366] re5 :1
re6 :1
re7 :1
re8 :1
复制代码
分析
为什么结果是这样的呢 继续分析,打开 debug->debug workflow->always show disassembly
允许汇编调试
Xcode
中把macOS
的版本调到10.15
以下,或者iOS的版本13.0
以下。查看下汇编代码
源码分析:isKindOfClass
和isMemberOfClass
底层的实现都是objc_msgSend
消息转发。通过SEL
找到对应的IMP
Xcode
中把macOS
的版本调到10.15
以上,或者iOS的版本13.0
以上。查看下汇编代码
源码分析:isKindOfClass
底层的实现objc_opt_isKindOfClass
,class底层的实现objc_opt_class
,isMemberOfClass
还是走消息转发
isKindOfClass 底层实现
//类方法 对应re1 跟 re3
+ (BOOL)isKindOfClass:(Class)cls {
//tcls = 类的元类self->ISA()
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
// 类的元类tcls vs cls传入的需要比较的类
if (tcls == cls) return YES;
}
return NO;
}
//对象方法 对应re5 跟 re7
- (BOOL)isKindOfClass:(Class)cls {
//tcls = 类
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
// 类tcls vs cls传入的需要比较的类
if (tcls == cls) return YES;
}
return NO;
}
复制代码
分析:
+isKindOfClass
流程。类的元类
vscls(需要比较的类)
,不同继续比较 。元类的父类
vscls
,不同继续比较直到找到根元类
。根元类 vs cls
,不同继续比较。根类(NSObject)
vscls
,如果还不相同则根类(NSObject)的父类
为nil
,跳出循环返回NO-isKindOfClass
流程。获取当前对象所属类
,类
vscls
,不同继续比较 。类的父类
vscls
,不同继续比较直到找到根类(NSObjec)
。根类(NSObject)
vscls
,如果还不相同则根类(NSObject)的父类
为nil
,跳出循环返回NO
isMemberOfClass底层实现
//类方法 对应re2 跟 re4
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
//对象方法 对应re6 跟 re8
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
复制代码
分析:
-
+isMemberOfClass
流程。类的元类
vscls(需要比较的类)
,相同就返回YES,否则返回NO -
-isMemberOfClass
流程。类
vscls(需要比较的类)
,相同就返回YES,否则返回NO
objc_opt_isKindOfClass 底层实现
objc_opt_isKindOfClass(id _Nullable obj, Class _Nullable cls)
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
// Calls [obj isKindOfClass]
BOOL
objc_opt_isKindOfClass(id obj, Class otherClass)
{
#if __OBJC2__ //现在基本上都用OBJC2版本
//slowpath(!obj) obj为空的是小概率事件基本不会发生
if (slowpath(!obj)) return NO;
// 获取类或者是元类:obj是对象就获取类,如果obj是类就获取元类
Class cls = obj->getIsa();
//fastpath(!cls->hasCustomCore()) (类或者父类中大概率没有默认的isKindOfClass方法)
if (fastpath(!cls->hasCustomCore())) {
for (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif //OBJC版本直接走消息转发
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
复制代码
分析
基本上都用OBJC2
版本,obj->getIsa()
获取类
或者元类
:obj是对象
就获取类
,obj是类
就获取元类
然后就接着for循环,for里面的代码和isKindOfClass
逻辑一样
objc_opt_Class 底层实现
OBJC_EXPORT Class _Nullable
objc_opt_class(id _Nullable obj)
OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0);
// Calls [obj class]
Class
objc_opt_class(id obj)
{
#if __OBJC2__
if (slowpath(!obj)) return nil;
// 获取类或者是元类:obj是对象就获取类,如果obj是类就获取元类
Class cls = obj->getIsa();
//(类或者父类中大概率没有默认的class方法)
if (fastpath(!cls->hasCustomCore())) {
//cls是类 返回cls,如果cls是元类,obj是类,返回obj还是类
return cls->isMetaClass() ? obj : cls;
}
#endif
return ((Class(*)(id, SEL))objc_msgSend)(obj, @selector(class));
}
复制代码
分析:objc_opt_class
的实现,其实就是获取类
,如果参数是对象
则返回类
,如果是类
就返回类
验证结果
- reg1:
+isKindOfClass
比较的是(NSObject的元类)NSObject
vsNSObject
,返回 1 - reg2:
+isMemberOfClass
比较的是NSObject的元类根元类
vsNSObject
,返回 0 - reg3:
+isKindOfClass
比较的是LGPerson的元类NSObject
vsLGPerson
,返回 0 - reg4:
+isMemberOfClass
比较的是LGPerson的元类NSObject
vsLGPerson
,返回 0 - reg5:
-isKindOfClass
比较的是NSObject对象所属类NSObject
vsNSObject
,返回 1 - reg6:
-isMemberOfClass
比较的是NSObject类
vsNSObject
,返回 1 - reg7:
-isKindOfClass
比较的是LGPerson对象所属类LGPerson
vsLGPerson
,返回 1 - reg8:
-isMemberOfClass
比较的是LGPerson类
vsLGPerson
,返回 1
总结
-
+ isKindOfClass
方法:元类-->元类的父类-->直到找到根元类
与cls
分别进行比较 -
- isKindOfClass
方法:类-->类的父类-->直到找到根类(NSObject)
与cls
分别进行比较 -
+ isMemberOfClass
方法:元类
vscls
-
- isMemberOfClass
方法:类
vscls