1. 下面代碼打印的結果是什么,并做分析。
int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
BOOL re3 = [(id)[NSObject class] isMemberOfClass:[NSObject 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);
}
return 0;
}
打印的結果:
re1 :1
re2 :0
re3 :0
re4 :0
為什么是這個結果呢?
** [[NSObject class] isKindOfClass:[NSObject class]]
,首先前面是類
在調用isKindOfClass
方法,所以找的是+ (BOOL)isKindOfClass:(Class)cls
類方法。**
我們查看源碼
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
對于re1
1.
Class tcls = self->ISA()
,NSObject類
的isa
指向根元類
,根元類
!=NSObject類
,循環繼續
2.接下來tcls = tcls->superclass
,根元類的父類
是NSObject類
, 此時tcls == cls
,returnYES
;
對于re2 **
1.
Class tcls = self->ISA()
,LGPerson信息類
的isa
指向LGHPerson的元類
,LGPerson的元類
!=LGPerson類
,循環繼續。
2.tcls = tcls->superclass
,LGPerson的元類的父類
是根元類
,根元類
!=LGPerson類
,循環繼續。
3.tcls = tcls->superclass
,根元類 的父類
是NSObject類
,NSObject類
!=LGPerson類
,循環繼續。
4.tcls = tcls->superclass
,NSObject的父類
是nil
,循環結束
5. returnNO
;
同理[(id)[NSObject class] isMemberOfClass:[NSObject class]]
,我們需要找的是+ (BOOL)isMemberOfClass:(Class)cls
類方法。
通過源碼得出:
+isMemberOfClass
是判斷當前類的isa所指向的類
與傳入的cls類
是否一致
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
對于re3
NSObject類的isa指向
是根元類
,根元類
!=NSObject類
,returnNO
;
對于re4
LGPerson類的isa
指向LGPerson的元類
,LGPerson的元類
!=LGPerson類
, returnNO
;
2. 下面代碼打印的結果是什么,并做分析。
int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject 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);
}
return 0;
}
打印的結果:
re5 :1
re6 :1
re7 :1
re8 :1
首先[(id)[NSObject alloc] isKindOfClass:[NSObject class]]
,我們應該找的是實例方法-(BOOL)isKindOfClass:(Class)cls
查看源碼:
1. 首先是tcls = [self class]
與cls
比較,
2. 接下來tcls->superclass(即父類)
與cls
比較,
3. tcls->superclass->superclass
(即父類的父類) 與cls
比較
.....for循環
4. 一直到NSObject類
與cls
比較,如果還是不相等,NSObject類->superclass
為nil
,for循環結束,返回NO
。
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
對于re5
Class tcls = [self class] ,tcls=NSObject類,NSObject類=NSObject類, return YES;
對于re6
Class tcls = [self class] ,tcls=LGPerson類,LGPerson類==LGPerson類,return YES;
對于[(id)[NSObject alloc] isMemberOfClass:[NSObject class]]
,我們該找的是實例方法-(BOOL)isMemberOfClass:(Class)cls*
查看源碼:
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
對于re7
Class tcls = [self class]
,tcls
=NSObject類
,NSObject類
==NSObject類
, return YES;
對于re8
Class tcls = [self class]
,tcls
=LGPerson類
,LGPerson類
==LGPerson類
,return YES;
2.以下代碼的輸出結果是什么?并分析原因。
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation LGPerson
- (void)sayHello{
NSLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"LGPerson say : Happy!!!");
}
@end
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
const char *className = class_getName(pClass);
Class metaPClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaPClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaPClass, @selector(sayHappy));
LGLog(@"method1=%p",method1);
LGLog(@"method2=%p",method2);
LGLog(@"method3=%p",method3);
LGLog(@"method4=%p",method4);
Method method5 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method6 = class_getInstanceMethod(metaPClass, @selector(sayHello));
Method method7 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method8 = class_getInstanceMethod(metaPClass, @selector(sayHappy));
LGLog(@"method5=%p",method5);
LGLog(@"method6=%p",method6);
LGLog(@"method7=%p",method7);
LGLog(@"method8=%p",method8);
}
return 0;
}
打印結果:
method1=0x0
method2=0x0
method3=0x100003070
method4=0x100003070
method5=0x1000030d8
method6=0x0
method7=0x0
method8=0x100003070
首先我們知道,
實例方法
存儲在類的實例方法列表中
中,而類方法
存儲在元類的實例方法列表
中。
查看
class_getClassMethod
的源代碼.
從源碼得出:
1. 首先它會去找cls的元組
的實例方法列表
,結合getMeta()
方法,如果本身是元組類型
,直接去找自身的實例方法列表
,
2. 然后cls->getMeta()->superclass
,去cls的元組的父類
里面找,
..... for循環..... ,
3. 一直找到根元類
,然后是NSObject類
,最后是nil
,結束循環。
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
// 如果是元組類型,直接返回,否則返回當前class的isa指向的類
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
Method class_getInstanceMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);
return _class_getMethod(cls, sel);
}
static Method _class_getMethod(Class cls, SEL sel)
{
mutex_locker_t lock(runtimeLock);
return getMethod_nolock(cls, sel);
}
getMethod_nolock(Class cls, SEL sel)
{
method_t *m = nil;
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
while (cls && ((m = getMethodNoSuper_nolock(cls, sel))) == nil) {
cls = cls->superclass;
}
return m;
}
class_getClassMethod源碼大致流程圖:
sayHello
是實例方法
,所以存在于LGPerson類的實例方法列表
中
1. method1,所以在LGPerson元類的methods
中獲取不到,返回0x00
2. method2, sayHello是實例方法,所以在LGPerson元類的methods
中就更加獲取不到,返回0x00
sayHappy
是類方法
,所以存在于LGPerson元類的實例方法列表
中.
1. method3, 會去找LGPerson元類的實例方法列表
中尋找,恰好就存在sayHappy,所以method3有值
。
2. method4, 也是會在會在找LGPerson元類的實例方法列表
中尋找,所以method4有值
。
接下來我們看
class_getInstanceMethod
的源碼.
從源碼得出:先從cls類的methods列表
中查找是否存在sel,然后從cls類的父類
的methods列表查找.... 直到從NSObject類的methods列表
查找
Method class_getInstanceMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);
return _class_getMethod(cls, sel);
}
static Method _class_getMethod(Class cls, SEL sel)
{
mutex_locker_t lock(runtimeLock);
return getMethod_nolock(cls, sel);
}
static method_t *
getMethod_nolock(Class cls, SEL sel)
{
method_t *m = nil;
runtimeLock.assertLocked();
ASSERT(cls->isRealized());
while (cls && ((m = getMethodNoSuper_nolock(cls, sel))) == nil) {
cls = cls->superclass;
}
return m;
}
sayHello是實例方法,存在于
LGPerson類的methods列表
中。
所以 method05有值,method6是0x00。
sayHappy是類方法,存在于
LGPerson元類的methods列表
中.
所以 method07是0x00
,method8有值
。
【拓展】3.以下代碼輸出的結果是什么?
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation LGPerson
- (void)sayHello{
NSLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"LGPerson say : Happy!!!");
}
@end
@interface LGTeacher : LGPerson
- (void)say666;
@end
@implementation LGTeacher
- (void)say666{
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGTeacher *teacher = [LGTeacher alloc];
Class pClass = object_getClass(teacher);
const char *className = class_getName(pClass);
Class metaPClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaPClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaPClass, @selector(sayHappy));
Method method5 = class_getClassMethod(pClass, @selector(say666));
Method method6 = class_getClassMethod(metaPClass, @selector(say666));
LGLog(@"method1=%p",method1);
LGLog(@"method2=%p",method2);
LGLog(@"method3=%p",method3);
LGLog(@"method4=%p",method4);
LGLog(@"method5=%p",method5);
LGLog(@"method6=%p",method6);
Method method7 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method8 = class_getInstanceMethod(metaPClass, @selector(sayHello));
Method method9 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method10 = class_getInstanceMethod(metaPClass, @selector(sayHappy));
Method method11 = class_getInstanceMethod(pClass, @selector(say666));
Method method12 = class_getInstanceMethod(metaPClass, @selector(say666));
LGLog(@"method5=%p",method7);
LGLog(@"method6=%p",method8);
LGLog(@"method7=%p",method9);
LGLog(@"method8=%p",method10);
LGLog(@"method7=%p",method11);
LGLog(@"method8=%p",method12);
}
return 0;
}
首先sayHello是實例方法
,存在于LGPerson類的methods列表
中。
對于method1,class_getClassMethod(
LGTeacher
, @selector(sayHello
)) ,
1. 首先去LGTeacher的元類
里面找,沒有找到,
2. 接下來去LGTeacher的元類的superclass
,即LGPerson的元類
里面去找,沒有找到,
3. 之后去根元類
和NSObject類
尋找,更是不會找到,返回0x00
。
對于method2 ,class_getClassMethod(
LGTeacher的元類
, @selector(sayHello
)),
1. 因為傳入的是LGTeacher的元類
,所以直接到LGTeacher的元類
里面找,沒有找到,
2. 接下來去LGTeacher的元類的superclass
,即LGPerson的元類
里面去找,沒有找到,
3. 之后去根元類
和NSObject類
尋找,更是不會找到,返回0x00。
首先sayHappy是類方法,存在于LGPerson元類的methods列表中。
對于method3 ,class_getClassMethod(LGTeacher, @selector(sayHappy)),
1. 首先去LGTeacher的元類里面實例方法列表
中找,沒有找到,
2. 接下來去LGTeacher的元類的superclass
,即LGPerson的元類里面的實例方法列表
中尋找,可以找到,所以method3有值
。
對于method4來說 ,
1. 首先去LGTeacher的元類里面實例方法列表
中找,沒有找到,
2. 接下來去LGTeacher的元類的superclass
,即LGPerson的元類
里面的實例方法列表中尋找,可以找到,所以method4有值
。
首先say666是實例方法,存在于LGTeacher類的實例方法列表中
所以method5 和 method6都為 0x00.
首先sayHello是實例方法,存在于LGPerson類的實例方法列表
中
對于method7 來說,
1. 去LGTeacher類的實例方法列表
中找,沒有找到,
2. 然后去LGPerson類的實例方法列表
中找,找到了,method7有值。
對于method8 ,
1. 去LGTeacher元類的實例方法列表
中找,沒有找到,
2. 然后去LGPerson元類的實例方法列表
中找,沒有找到,
3. 最后去根元類和NSObject類的實例方法列表
尋找,沒有找到,method8=0x00
首先sayHappy是類方法,存在于LGPerson元類的實例方法列表
中。
對與method9,
1. 去LGTeacher類的實例方法列表
中找,沒有找到,
2. 然后去LGPerson類的實例方法列表
中找,沒有找到了,3. 最后去NSObject類的實例方法列表
尋找,沒有找到,method9=0x00
對與method10,
1. 去LGTeacher元類的實例方法列表
中找,沒有找到,
2. 然后去LGPerson元類的實例方法列表
中找,找到了,method10有值
首先say666是實例方法,存在于LGTeacher類的實例方法列表
中
所以method11有值,method12=0x00
**打印結果: **
method1=0x0
method2=0x0
method3=0x100003070
method4=0x100003070
method5=0x0
method6=0x0
method7=0x1000030d8
method8=0x0
method9=0x0
method10=0x100003070
method11=0x100003188
method12=0x0
4.下面的代碼輸出什么?
@implementation Son : Father
- (id)init {
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
結果: Son / Son
分析:
對于上面的答案,第一個的結果應該是我們的預期結果,但是第二個結果卻讓我們很費解了。
那我們利用前面文章講過的知識點來分析一下整個的流程。
因為,Son 及 Father 都沒有實現 -(Class)calss 方法,所以這里所有的調用最終都會找到基類 NSObject 中,并且在其中找到 -(Class)calss 方法。那我們需要了解的就是在 NSObject 中這個方法的實現了。
在 NSObject.mm 中可以找到 -(Class)class 的實現:
- (Class)class {
return object_getClass(self);
}
在 objc_class.mm 中找到 object_getClass 的實現:
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
ps:上面的方法定義可以去可編譯調試的objc4源碼中下載源碼哦。
可以看到,最終這個方法返回的是,調用這個方法的 objc 的 isa 指針。那我們只需要知道在題干中的代碼里面最終是誰在調用 -(Class)class 方法就可以找到答案了。
接下來,我們利用 clang -rewrite-objc 命令,將題干的代碼轉化為如下代碼:
NSLog((NSString *)&__NSConstantStringImpl__var_folders_8k_cgm28r0d0bz94xnnrr606rf40000gn_T_Car_3f2069_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_8k_cgm28r0d0bz94xnnrr606rf40000gn_T_Car_3f2069_mi_1, NSStringFromClass(((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Car"))}, sel_registerName("class"))));
從上方可以得出,調用 Father class 的時候,本質是在調用
objc_msgSendSuper(struct objc_super *super, SEL op, ...)
struct objc_super 的定義如下:
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver;
/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
/* super_class is the first class to search */
};
從定義可以得知:當利用 super 調用方法時,只要編譯器看到super這個標志,就會讓當前對象去調用父類方法,本質還是當前對象在調用,是去父類找實現,super 僅僅是一個編譯指示器。但是消息的接收者 receiver 依然是self。最終在 NSObject 獲取 isa 指針的時候,獲取到的依舊是 self 的 isa,所以,我們得到的結果是:Son。
5. 看看下方的代碼會輸出什么?
@interface Father : NSObject
@end
@implementation Father
- (Class)class {
return [Father class];
}
@end
---
@interface Son : Father
@end
@implementation Son
- (id)init {
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
int main(int argc, const char * argv[]) {
Son *foo = [[Son alloc]init];
return 0;
}
---輸出:---
Father
Father
更多的面試題和答案:https://github.com/iOSputao/iOS-