前言
今天我們結合前面所學的OC底層來對經典的面試題進行分析,將學習成果運用到實踐中。
目錄
1、簡介
在日常開發中我們經常用到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]];
[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。按照如下圖標紅部分進行比較:
2.2.1、 [[NSObject class] isKindOfClass:[NSObject class]]
BOOL re3 = [[NSObject class] isKindOfClass:[NSObject class]];
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]];
從上圖可以看到,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。