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-