前言
书接上回类的结构分析,我们得知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);
}
下篇文章揭晓,哈哈!












网友评论