前言
今天我们结合前面所学的OC底层来对经典的面试题进行分析,将学习成果运用到实践中。
目录
目录.png1、简介
在日常开发中我们经常用到isKindOfClass或者isMemberOfClass的实例方法或者类方法,其是由RunTime提供封装的Api,但是我们是否深入理解和掌握了呢?
1.1、经典面试题
看看下面的代码输出什么
// Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
@end
// Person.m
#import "Person.h"
@implementation Person
@end
// main.m
#import <Foundation/Foundation.h>
#import "Person.h"
#import <objc/runtime.h>
#import <malloc/malloc.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL re1 = [[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re2 = [[Person alloc] isKindOfClass:[Person class]];
BOOL re3 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL re4 = [[Person class] isKindOfClass:[Person class]];
NSLog(@" re1 :%d\n re2 :%d\n re3 :%d\n re4 :%d\n",re1,re2,re3,re4);
BOOL re5 = [[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re6 = [[Person alloc] isMemberOfClass:[Person class]];
BOOL re7 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re8 = [[Person class] isMemberOfClass:[Person class]];
NSLog(@" re5 :%d\n re6 :%d\n re7 :%d\n re8 :%d\n",re5,re6,re7,re8);
}
return 0;
上面是关于isKindOfClass 和 isMemberOfClass的经典分析题目,输出结果你答对了么?
re1 :1
re2 :1
re3 :1
re4 :0
re5 :1
re6 :1
re7 :0
re8 :0
上面的题目实际上考察了OC底层的isa、superclass走位以及isKindOfClass&isMemberOfClass的源码如何实现,
让我们回顾一下isa和superclass走位图,对源码分析的时候需要用到
有了前面的《OC底层》的学习的知识,并结合上图以及源码,可以很好的分析了。
2、isKindOfClass
2.1、- (BOOL)isKindOfClass:
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
根据源码和isa走位图可以知道,- (BOOL)isKindOfClass:用于判断当前实例所属的类是否==cls,不相等则沿着superclass走位一直遍历比较到NSObject类,路径上有相等的则返回false,遍历结束均不相等则返回false。
其对比路径如下图红色部分:
2.1.1、[[NSObject alloc] isKindOfClass:[NSObject class]]
BOOL re1 = [[NSObject alloc] isKindOfClass:[NSObject class]];
比较过程如下图所示
tcls初始值为[[NObject alloc] class],即[NSObject class],因此re1==1;
2.1.2、[[Person alloc] isKindOfClass:[Person class]]
BOOL re2 = [[Person alloc] isKindOfClass:[Person class]];
re2.png
[Person alloc]的isa指向Person类,即[Person class],因此re2==1;
2.2、+ (BOOL)isKindOfClass
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
根据源码和isa走位图可以知道,+ (BOOL)isKindOfClass:用于判断当前类的元类是否==cls,不相等则从该类的元类开始,沿着superclass一直遍历比较到NSObject类是否==cls,有相等的则返回true,遍历结束均不相等返回false。按照如下图标红部分进行比较:
+ (BOOL)isKindOfClass:2.2.1、 [[NSObject class] isKindOfClass:[NSObject class]]
BOOL re3 = [[NSObject class] isKindOfClass:[NSObject class]];
re3.png
tcls初始值为[NSObject class]->ISA(),即NSObject元类,NSObject元类的superclass又指向[NSObject class],因此re3==1;
2.2.2、 [[Person class] isKindOfClass:[Person class]]
BOOL re4 = [[Person class] isKindOfClass:[Person class]];
re4.png
从上图可以看到,tcls初始值为[Person class]->ISA(),即Person元类,一直比较遍历到NSObject类,均不等于[Person class],因此re4==0;
3、isMemberOfClass
3.1、- (BOOL)isMemberOfClass:
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
比较当前实例的类是否==cls。
3.1.1、[[NSObject alloc] isMemberOfClass:[NSObject class]]
BOOL re5 = [[NSObject alloc] isMemberOfClass:[NSObject class]];
[self class] = [[NSObject alloc] class] == [NSObject class],
cls=[NSObject class],所以 [self class] == cls == 1;
3.1.2、[[Person alloc] isMemberOfClass:[Person class]];
BOOL re6 = [[Person alloc] isMemberOfClass:[Person class]];
[self class]= [[Person alloc] class] == [Person class] == 1;
cls=[Person class],所以 [self class] == cls == 1;
3.2、+ (BOOL)isMemberOfClass:
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
比较当前类的元类是==cls。
3.2.1、[[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re7 = [[NSObject class] isMemberOfClass:[NSObject class]];
根据isa走位图,[[NSObject class]->ISA()为NSObject的元类,不等于[[NSObject class],因此re6==0;
3.2.2、[[Person class] isMemberOfClass:[Person class]];
BOOL re8 = [[Person class] isMemberOfClass:[Person class]];
根据isa走位图,[[Person class]->ISA()为Person元类,不等于[[Person class],因此re8==0;
4、注意
实际运行源码调试的时候会发现,isMemberOfClass会走到源码处,但是isKindOfClass不会,isKindOfClass的示例方法和类方法实际上调用的都是** 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->superclass) {
if (tcls == otherClass) return YES;
}
return NO;
}
#endif
return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}
原因在于llvm中编译时对其进行了优化处理。
5、总结
- - (BOOL)isKindOfClass:用于判断当前实例所属的类是否==cls,不相等则沿着superclass走位一直遍历比较到NSObject类,路径上有相等的则返回false,遍历结束均不相等则返回false。
- + (BOOL)isKindOfClass:用于判断当前类的元类是否==cls,不相等则从该类的元类开始,沿着superclass一直遍历比较到NSObject类是否==cls,有相等的则返回true,遍历结束均不相等返回false。
- -(BOOL)isMemberOfClass:比较当前实例的类是否==cls。
- +(BOOL)isMemberOfClass:比较当前类的元类是否==cls。
网友评论