前言
书接上回类的结构分析,我们得知isa
指针的走位:对象 --> 类 --> 元类 --> 根元类-->根元类 指向自身
。下面看看关于isa
指针相关2道经典面试题。
准备
先定义一个示例类LGPerson
:
@interface LGPerson : NSObject
- (void)sayHello;
+ (void)sayByebye;
@end
@implementation LGPerson
- (void)sayHello {
NSLog(@"LGPerson: say hello!");
}
+ (void)sayByebye {
NSLog(@"LGPerson: say bye!");
}
@end
面试题一:isKindOfClass
& isMemberOfClass
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
BOOL re3 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re4 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
BOOL re5 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re6 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];
BOOL re7 = [(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);
坑点分析:
isKindOfClass
和isMemberOfClass
的区别- 请注意
isKindOfClass
和isMemberOfClass
是类方法
还是实例方法
1.底层分析isKindOfClass
先看实例方法:
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
很简单,当前实例对象的类[self class]
与传递的入参类cls
是否相等,是则返回YES
,否则继续向上找父类 【for循环tcls->superclass】
,直到找到为止,否则返回NO
。
for循环的方向是类-->父类-->(n 步)-->根类NSObject-->nil(此时for循环终止)
再看类方法:
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
与实例方法不同,类方法是当前类对象的self->ISA()
,即元类,与传递的入参类cls
是否相等,是则返回YES
,否则继续向上找父类 【元类的父类】
,继续【根元类】
,继续【根元类的父类NSObject】
,继续【NSObject的父类】是nil
,循环终止,返回NO。
for循环的方向是元类-->元类的父类-->(n 步)-->根元类-->根元类的父类NSObject-->nil(此时for循环终止)
2.底层分析 isMemberOfClass
同样看源码:
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- 实例方法:判断当前的
类[self class]
是否和入参类cls
相等。 - 类方法是:判断当前的
元类self->ISA()
是否和入参类cls
相等。
3.解答
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
BOOL re3 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re4 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];
- re1 和 re2的
isKindOfClass
是类方法,根据for循环方向
元类-->元类的父类-->(n 步)-->根元类-->根元类的父类NSObject-->nil(此时for循环终止)
-
[NSObject class]
的元类是根元类,根元类的父类就是[NSObject class]
,二者相等,结果为YES。 -
[LGPerson class]
的元类[LGPerson meta class]
,二者不相等,继续循环,[LGPerson meta class] ->superclass = [LGPerson root meta class]
,也不相等,继续循环,[LGPerson root meta class]->superclass = NSObject根类
,不相等,继续循环,NSObject根类->superclass = nil
,循环终止,返回NO。
- re3 和 re4是实例方法,根据for循环方向
父类-->(n 步)-->根类NSObject-->nil(此时for循环终止)
-
[NSObject alloc]
的类是[NSObject class]
,与入参[NSObject class]
相同,返回YES。 -
[LGPerson alloc]
的类是[LGPerson class]
,与入参[LGPerson class]
相同,返回YES。
再看isMemberOfClass
BOOL re5 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re6 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];
BOOL re7 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];
- re5 和 re6的
isMemberOfClass
是类方法,根据
元类self->ISA()
是否和入参类cls
相等
-
[NSObject class]
的元类是`根元类,不同,返回NO。 -
[LGPerson class]
的元类是[LGPerson meta class]
,不相同,返回NO。
- re7 和 re8的
isMemberOfClass
是实例方法,根据
类[self class]
是否和入参类cls
相等
-
[NSObject alloc]
的类是[NSObject class]
,与入参[NSObject class]
相等,YES。 -
[LGPerson alloc]
的类是[LGPerson class]
, 与入参[LGPerson class]
相等,YES。
面试题二:实例方法与类方法的归属
根据isa指针的走位,我们知道,实例方法存储在类中,而类方法存储在元类中
。
1.class_getInstanceMethod
是获取类的实例方法
void lgInstanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayByebye));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayByebye));
NSLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
1.LGPerson类
中有实例方法- (void)sayHello;
,所以method1有值,method2是元类中判断,所以无值
2.LGPerson类
中的类方法+ (void)sayByebye;
是在[LGPerson meta class]
中,所以method3无值,method4有值
2.class_getClassMethod
是获取类的类方法
void lgClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayByebye));
Method method4 = class_getClassMethod(metaClass, @selector(sayByebye));
NSLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
-
- (void)sayHello;
是实例方法,所以method1 和 method2都不成立,无值。 -
+ (void)sayByebye;
是类方法,看看method3,method4的值如何?
运行看看
> - 0x0 -- 0x0 -- 0x100003148 -- 0x100003148
method3 method4都有值,why?查看源码:
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
可见,获取 类方法 就是获取 元类中的 实例方法,所以method3有值。
再看看cls->getMeta()
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
可见,在获取元类的时候,如果入参是元类,那么直接返回;否则才会继续this->ISA()
查找元类。所以,method4表达式中,传入的已经是 Person 的元类, 那么会直接返回,而不是去找 Person元类的元类 。而 Person 元类中就是存在 + (void)sayByebye
这样一个类方法的,所以 method4 是有值的。
延伸:imp
请思考,以下方法打印什么?
void lgIMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayByebye));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayByebye));
NSLog(@"%s-%p-%p-%p-%p",__func__,imp1,imp2,imp3,imp4);
}
下篇文章揭晓,哈哈!
网友评论