面试题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。针对该方法,苹果有如下说明
image.png
根据之前的分析,
实例方法是存储在对象所属的类中。
类方法是存储元类中。
分析
- 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。针对该方法,苹果有如下说明
image.png
分析
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
总结
实例方法存储在对象所属的类中类方法存储在元类中在元类中,类方法是以实例方法的形式存储的对象是所属类的实例类是元类的实例











网友评论