面试题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 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);
}
return 0;
}
isKindOfClass
类方法isKindOfClass
该方法主要是用来判断当前类的元类
与条件类
是否相等。
- 如果相等,返回YES;
- 如果不相等,则继续查找
该元类的父类
,使之与条件类
进行比较。
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
实例方法isKindOfClass
该方法主要是用来判断当前对象所属的类
与条件类
是否相等。
- 如果相等,则返回YES;
- 如果不相等,则继续查找
该类的父类
,使之与条件类
进行比较。
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
示例1
[(id)[NSObject class] isKindOfClass:[NSObject class]]
- 通过
[NSObject class]
分析得知,调用的是类方法isKindOfClass
。 - 第一次判断
cls
为NSObject类
;
tcls
为NSObject元类
;
cls
不等于tcls
。
修改tcls
为NSObject元类的父类
,即:NSObject类
。 - 第二次判断
cls
为NSObject类
;
tcls
为NSObject类
;
cls
等于tcls
。
结果:返回YES
。
示例2
[(id)[LGPerson class] isKindOfClass:[LGPerson class]]
- 通过
[LGPerson class]
分析得知,调用的是类方法isKindOfClass
。 - 第一次判断
cls
为LGPerson类
;
tcls
为LGPerson元类
;
cls
不等于tcls
。
修改tcls
为LGPerson元类的父类
,即根元类NSObject
。 - 第二次判断
cls
为LGPerson类
;
tcls
为根元类
;
cls
不等于tcls
。
修改tcls
为NSObject元类的父类
,即:NSObject类
。 - 第三次判断
cls
为LGPerson类
;
tcls
为NSObject类
;
cls
不等于tcls
。
修改tcls
为NSObject类的父类
,即:nil
。 - 由于
tcls
为nil
,循环结束。
结果:返回NO
;
示例3
[(id)[NSObject alloc] isKindOfClass:[NSObject class]]
- 通过
[NSObject alloc]
分析得知,调用的是对象方法isKindOfClass
。 - 第一次判断
cls
为NSObject类
;
tcls
为NSObject类
;
cls
等于tcls
。
结果:返回YES
。
示例4
[(id)[LGPerson alloc] isKindOfClass:[LGPerson class]]
- 通过
[LGPerson alloc]
分析得知,调用的是对象方法isKindOfClass
。 - 第一次判断
cls
为LGPerson类
;
tcls
为LGPerson类
;
cls
等于tcls
。
结果:返回YES
。
isMemberOfClass
类方法isMemberOfClass
该方法主要用来判断当前类的元类
与条件类
是否相等。
- 如果相等,则返回YES;
- 如果不相等,则返回NO;
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
实例方法isMemberOfClass
该方法主要用来判断当前对象所属的类
与条件类
是否相等。
- 如果相等,则返回YES;
- 如果不相等,则返回NO;
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
示例5
[(id)[NSObject class] isMemberOfClass:[NSObject class]]
- 通过
[NSObject class]
分析得知,调用的是类方法isMemberOfClass
。 -
当前元类
为NSObject元类
,条件类
为NSObject
。
结果:不相等
,返回NO
。
示例6
[(id)[LGPerson class] isMemberOfClass:[LGPerson class]]
- 通过
[LGPerson class]
分析得知,调用的是类方法isMemberOfClass
。 -
当前元类
为LGPerson类的元类
,条件类
为LGPerson类
。
结果:不相等
,返回NO
。
示例7
[(id)[NSObject alloc] isMemberOfClass:[NSObject class]]
- 通过
[NSObject alloc]
分析得知,调用的是实例方法isMemberOfClass
。 -
当前对象类
为NSObject类
,条件类
为NSObject类
。
结果:相等
,返回YES
。
示例8
[(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]]
- 通过
[LGPerson alloc]
分析得知,调用的是实例方法isMemberOfClass
。 -
当前对象类
为LGPerson类
,条件类
为LGPerson类
。
结果:相等
,返回YES
。
面试题1输出结果:
2020-09-30 14:59:08.857321+0800 KCObjc[72747:9401735]
re1 :1
re2 :0
re3 :1
re4 :1
2020-09-30 14:59:12.976812+0800 KCObjc[72747:9401735]
re5 :0
re6 :0
re7 :1
re8 :1
Program ended with exit code: 0
总结
- 类方法 isKindOfClass 和 isMemberOfClass
相同:比较 元类 与条件类。
不同:isKindOfClass 在第一次比较不成功之后,会继续查找元类的父类,直至找到nil,而 isMemberOfClass 在比较不成功之后直接返回false。 - 实例方法 isKindOfClass 和 isMemberOfClass
相同:比较 类与 条件类
不同:isKindOfClass 在第一次比较不成功之后,会继续查找类的父类,直至找到nil,而 isMemberOfClass 在比较不成功之后直接返回false。
面试题2
创建一个 Person 类,继承自 NSObject,分别添加一个实例方法和一个类方法
@interface LCPerson : NSObject
- (void)sayHello;
+ (void)say666;
@end
@implementation LCPerson
- (void)sayHello{
NSLog(@"sayHello");
}
+ (void)say666{
NSLog(@"say666");
}
@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 metaClass = objc_getMetaClass(className);
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
NSLog(@"method1 :%p\n method2 :%p\n method3 :%p\n method4 :%p\n",method1,method2,method3,method4);
}
return 0;
}
class_getInstanceMethod
该方法主要是用于获取实例方法
,如果在传入的类或者类的父类中没有找到指定的实例方法,则返回NULL
。针对该方法,苹果有如下说明
根据之前的分析,
实例方法是存储在对象所属的类中
。
类方法是存储元类中
。
分析
- method1
目的:在LGPerson类
中查找sayHello的实例方法
依据:实例方法是存储在对象所属的类中
实际:在LGPerson类
中有定义sayHello的实例方法
结果:method1是存在的。 - method2
目的:在LGPerson的元类
中查找sayHello的实例方法
依据:实例方法是存储在对象所属的类中
实际:sayHello的实例方法
是存在LGPerson类
中
结果:method2是不存在的。 - method3
目的:在LGPerson类
中查找sayHappy的实例方法
依据:实例方法是存储在对象所属的类中
实际:在LGPerson类
中没有定义sayHappy的实例方法
结果:method3是不存在的。 - method4
目的:在LGPerson的元类
中查找sayHappy的实例方法
依据:实例方法是存储在对象所属的类中
实际:在LGPerson类
中没有定义sayHappy的实例方法
,但是由于LGPerson类
定义了sayHappy的类方法
,而类方法
是以实例方法的形式
存储在元类
中。
结果:method4是存在的。
面试题2输出结果:
2020-09-30 15:01:41.051611+0800 KCObjc[72806:9404232]
method1 :0x100008100
method2 :0x0
method3 :0x0
method4 :0x100008098
Program ended with exit code: 0
面试题3
此题是对面试题2的延伸,因此准备条件与面试题2一样。
分析以下代码分别输出什么?
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
Class pClass = object_getClass(person);
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(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
NSLog(@"method1 :%p\n method2 :%p\n method3 :%p\n method4 :%p\n",method1,method2,method3,method4);
}
return 0;
}
class_getClassMethod
该方法主要是用于获取类方法
,一个指向方法数据结构的指针
,它指向类指定的类方法的实现
,如果指定的类或它的父类不包含具有指定的类方法,则为NULL
。针对该方法,苹果有如下说明
分析
Method class_getClassMethod(Class cls, SEL sel) {
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
由源码知道,获取类方法其实就是获取元类的实例方法
。
method1
传入的参数是LGPerson类
,但在class_getClassMethod
中,先根据LGPerson类
获取LGPerson元类
,然后再找LGPerson元类
中的实例方法sel
。此时sel
为sayHello方法
。
在LGPerson元类
中只存储了sayHappy 方法
。
结果:method1是不存在的。method2
传入的参数是LGPerson元类
,此时查找的是LGPerson元类
中的实例方法sel
。此时sel
为sayHello方法
。
在LGPerson元类
中只存储了sayHappy 方法
。
结果:method2是不存在的。method3
传入的参数是LGPerson类
,但在class_getClassMethod
中,先根据LGPerson类
获取LGPerson元类
,然后再找LGPerson元类
中的实例方法sel
。此时sel
为sayHappy方法
。
在LGPerson元类
中存储了sayHappy 方法
。
结果:method3是存在的。method4
传入的参数是LGPerson元类
,此时查找的是LGPerson元类
中的实例方法sel
。此时sel
为sayHappy方法
。
在LGPerson元类
中存储了sayHappy 方法
。
结果:method4是存在的。
面试题3输出结果:
2020-09-30 15:12:42.559984+0800 KCObjc[72925:9410227]
method1 :0x0
method2 :0x0
method3 :0x100008098
method4 :0x100008098
Program ended with exit code: 0
总结
实例方法存储在对象所属的类中
类方法存储在元类中
在元类中,类方法是以实例方法的形式存储的
对象是所属类的实例
类是元类的实例