面试题
话不多说,通过一个面试题来探究下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的元类)NSObjectvsNSObject,返回 1 - reg2:
+isMemberOfClass比较的是NSObject的元类根元类vsNSObject,返回 0 - reg3:
+isKindOfClass比较的是LGPerson的元类NSObjectvsLGPerson,返回 0 - reg4:
+isMemberOfClass比较的是LGPerson的元类NSObjectvsLGPerson,返回 0 - reg5:
-isKindOfClass比较的是NSObject对象所属类NSObjectvsNSObject,返回 1 - reg6:
-isMemberOfClass比较的是NSObject类vsNSObject,返回 1 - reg7:
-isKindOfClass比较的是LGPerson对象所属类LGPersonvsLGPerson,返回 1 - reg8:
-isMemberOfClass比较的是LGPerson类vsLGPerson,返回 1
总结
-
+ isKindOfClass方法:元类-->元类的父类-->直到找到根元类与cls分别进行比较 -
- isKindOfClass方法:类-->类的父类-->直到找到根类(NSObject)与cls分别进行比较 -
+ isMemberOfClass方法:元类vscls -
- isMemberOfClass方法:类vscls