和谐学习!不急不躁!!我是你们的老朋友小青龙~
作为iOS一名开发人员,我们平时经常会用到==
、isKindOfClass
、isMemberOfClass
,但是你知道它们是怎么使用的吗?以及在底层是如何实现的吗?OK,我们先上案例:
【友情提示,前面的源码分析先挖个坑~】
探索 - - - - - - - - - - - - ==
先创建两个类:
#pragma mark -- 创建一个Person类,继承自NSObject
@interface Person:NSObject
@property (nonatomic,strong) NSString *name;
-(void)eatFood;
@end
@implementation Person
-(void)eatFood{}
@end
#pragma mark -- 创建一个Student类,继承自Person
@interface Student:Person
@property (nonatomic,strong) NSString *age;
-(void)run;
@end
@implementation Student
-(void)run{}
@end
接着进行比较:
#pragma mark -------- ==比较 ---------
void compareWithDD(){
NSObject *n1 = [[NSObject alloc] init];
NSObject *n2 = [[NSObject alloc] init];
Person *p1 = [[Person alloc] init];
Person *p2 = [[Person alloc] init];
Student *s1 = [[Student alloc] init];
Student *s2 = [[Student alloc] init];
// ==比较
NSLog(@"\n-------- ==打印 --------");
NSLog(@"n1 == n2 -->%d",n1 == n2);
NSLog(@"p1 == p2 -->%d",p1 == p2);
NSLog(@"s1 == s2 -->%d",s1 == s2);
NSLog(@"");
NSLog(@"n1.class == n2.class -->%d",n1.class == n2.class);
NSLog(@"p1.class == p2.class -->%d",p1.class == p2.class);
NSLog(@"s1.class == s2.class -->%d",s1.class == s2.class);
}
#pragma mark -- main函数入口
int main(int argc, char * argv[]) {
compareWithDD();
...
}
打印结果:
我们发现同一个类的不同对象进行比较,==比较的结果是false。这是为什么呢?我们打印一下n1、n2的地址,以及它们指向的值的地址。
得出结论:==符号的比较,是两个指针地址的比较
。
探索 - - - - - - - - - - - - isKindOfClass
#pragma mark -------- isKindOfClass比较 ---------
void compareWithIsKindOfClass(void){
NSObject *n1 = [[NSObject alloc] init];
NSObject *n2 = [[NSObject alloc] init];
Person *p1 = [[Person alloc] init];
Student *s1 = [[Student alloc] init];
Class objectClass = NSObject.class;
Class studentClass = Student.class;
//isKindOfClass比较
NSLog(@"\n\n-------- isKindOfClass打印 --------");
NSLog(@"n1 isKindOfClass n2.class -->%d",[n1 isKindOfClass:n2.class]);
NSLog(@"n1 isKindOfClass p1.class -->%d",[n1 isKindOfClass:p1.class]);
NSLog(@"n1 isKindOfClass s1.class -->%d",[n1 isKindOfClass:s1.class]);
NSLog(@"p1 isKindOfClass s1.class -->%d",[p1 isKindOfClass:s1.class]);
NSLog(@"objectClass isKindOfClass objectClass -->%d",[objectClass isKindOfClass:objectClass]);
NSLog(@"studentClass isKindOfClass studentClass -->%d",[studentClass isKindOfClass:studentClass]);
}
#pragma mark -- main函数入口
int main(int argc, char * argv[]) {
compareWithIsKindOfClass();
...
}
打印结果:
我们发现n1和n2.class地址不一样,但是结果是YES;NSObject和NSObject两个类进行比较,结果是true,Student和Student两个类进行比较,结果却是false,这是为什么呢?
接下来,我们来探究一下isKindOfClass的底层实现,老规矩,打开objc4-818.2
源码,搜索“isKindOfClass
”,找到:
/**
类方法
1、这是一个for循环;
2、第一层循环:tcls等于self类的ISA指向(即元类),tcls是否等于传入的cls类,等于就返回YES,不等就进入下一次循环。
2、第二层循环:tcls等于tcls的父类,tcls是否等于传入的cls类,等于就返回YES,不等就进入下一次循环。
3、直到循环结束也没匹配到跟cls类相等的,返回NO;
*/
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
/**
实例方法
1、这是一个for循环;
2、第一层循环:tcls等于self的父类,tcls是否等于传入的cls类,等于就返回YES,不等就进入下一次循环。
3、第二层循环:tcls等于tcls的父类,tcls是否等于传入的cls类,等于就返回YES,不等就进入下一次循环。
4、直到循环结束也没匹配到跟cls类相等的,返回NO;
*/
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
回到前面的疑问
- n1和n2.class地址不一样,但是结果是YES;
n1是实例对象,会调用实例方法,进入循环:
第1遍--> tcls = [n1 class] ,跟n2.class一样,所以返回YES;
这也就解释通了n1和n2.class地址不一样,但结果是YES。
- NSObject和NSObject两个类进行比较,结果是true,Student和Student两个类进行比较,结果却是false。
类->走类方法,进入循环:
NSObject走向:
第1遍-->tcls = NSObject根元类,NSObject根元类不等于NSObject类;
第2遍-->tcls = NSObject根元类的ISA指向(即NSObject类),所以返回YES;
Student走向:
第1遍-->tcls = Student元类,Student元类不等于Student类;
第2遍-->tcls = Student元类的ISA指向(即`NSObject根元类`),不等;
第3遍-->tcls = NSObject根元类的ISA指向(即`NSObject类`),不等;
第4遍-->tcls = NSObject根元类的ISA指向(即`nil`),循环结束,返回NO;
探索 - - - - - - - - - - - - isMemberOfClass
#pragma mark -------- isMemberOfClass比较 ---------
void compareWithIsMemberOfClass(void){
NSObject *n1 = [[NSObject alloc] init];
Person *p1 = [[Person alloc] init];
Student *s1 = [[Student alloc] init];
Class objectClass = NSObject.class;
Class studentClass = Student.class;
//isMemberOfClass比较
NSLog(@"\n\n-------- isMemberOfClass打印 --------");
NSLog(@"n1 isMemberOfClass NSObject.class -->%d",[n1 isMemberOfClass:NSObject.class]);
NSLog(@"p1 isMemberOfClass NSObject.class -->%d",[p1 isMemberOfClass:NSObject.class]);
NSLog(@"s1 isMemberOfClass NSObject.class -->%d",[s1 isMemberOfClass:NSObject.class]);
NSLog(@"");
NSLog(@"n1 isMemberOfClass Person -->%d",[n1 isMemberOfClass:Person.class]);
NSLog(@"p1 isMemberOfClass Person -->%d",[p1 isMemberOfClass:Person.class]);
NSLog(@"s1 isMemberOfClass Person -->%d",[s1 isMemberOfClass:Person.class]);
NSLog(@"");
NSLog(@"n1 isMemberOfClass Student -->%d",[n1 isMemberOfClass:Student.class]);
NSLog(@"p1 isMemberOfClass Student -->%d",[p1 isMemberOfClass:Student.class]);
NSLog(@"s1 isMemberOfClass Student -->%d",[s1 isMemberOfClass:Student.class]);
NSLog(@"");
NSLog(@"objectClass isMemberOfClass objectClass -->%d",[objectClass isMemberOfClass:objectClass]);
NSLog(@"studentClass isMemberOfClass studentClass -->%d",[studentClass isMemberOfClass:studentClass]);
}
#pragma mark -- main函数入口
int main(int argc, char * argv[]) {
compareWithIsMemberOfClass();
...
}
打印结果:
擦看底层实现-->
打开
objc4-818.2
源码,搜索“isMemberOfClass
”,找到:
/**
返回self元类和cls类的比较结果
*/
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
/**
返回self类和cls类的比较结果
*/
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
我们可以看到,isMemberOfClass
和isKindOfClass
有点类似,只不过isMemberOfClass不走循环,只执行一次。
总结 - - - - - - - - - - - -
如果只是比较a和b的值,用 ==
关于isMemberOfClass的使用,如果是实例方法,走的是getSuperclass继承链;如果是类方法,走的是ISA指向链;
如果要判断实例对象a,是否为b类的实例对象,用isKindOfClass
百度网盘:
objc4-818.2源码
:pan.baidu.com/s/1qWbB4c7l…
密码:vkh4
Demo链接
:pan.baidu.com/s/1mdlJXc_6…
密码:9pds
注意 - - - - - - - - - - - -
别急着走,自己挖的坑还是要填一下的。
一顿操作下来,似乎没什么毛病,结论也可以解释我们的案例打印结果。
但是我们知道,oc代码到了底层,最终的实现是汇编,所以我们为了双重保险,应该先打开汇编调试-->
main方法加入测试代码:
int main(int argc, const char * argv[]) {
...
///探索isKindof底层实现
BOOL res = [NSObject.class isKindOfClass:NSObject.class];
NSLog(@"%d",res);
...
return 0;
}
在BOOL res = [NSObject.class isKindOfClass:NSObject.class];
这行打上断点,
打开汇编调试:Xcode -> Debug -> Debug Workflow -> Always show disassembly。
运行代码可以看到:
真是惊呆了我的小伙伴!底层实现竟然不是isKindOfClass:
OK,我们在objc源码搜索“objc_opt_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->getSuperclass()) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
/** 解释一下流程走向(结合isa走位图分析)
(ps:因为当前是OJBC2版本,所以endif那行代码可以忽略)
1、cls = obj的Isa指向,判断cls或cls的父类是否有isKindOf方法,有就进入第2步;
2、这是一个for循环,
第一遍循环:tcls等于cls,tcls是否等于传入的otherClass类,等于就返回YES,不等就进入下一次循环。
第二遍循环:tcls等于tcls的父类,tcls是否等于传入的otherClass类,等于就返回YES,不等就进入下一次循环。
3、直到循环结束也没匹配到跟otherClass类相等的,返回NO;
*/
废话连篇 - - - - - - - - - - - -
其实小编一开始也是想当然的认为,底层是isKindOf:
。
毕竟在objc源码工程里,确实存在着isKindOf:的类方法和实例方向,按照isa的指向链
和类的继承链
的确可以解释的通,直到被-->库细
大佬一句话否定了分析思路,我清晰的记得他老人家那句“要敢于质疑一切
”。最后还是老老实实的走了一下汇编调试。