面试题引发的思考:
Q: 以下代码的打印内容是什么?
// TODO: ----------------- Person类 -----------------
@interface Person : NSObject
@end
@implementation Person
@end
// TODO: ----------------- Student类 -----------------
@interface Student : Person
@end
@implementation Student
- (instancetype)init
{
self = [super init];
if (self) {
NSLog(@"[self class] = %@", [self class]);
NSLog(@"[self superclass] = %@", [self superclass]);
NSLog(@"---------------------------");
NSLog(@"[super class] = %@", [super class]);
NSLog(@"[super superclass] = %@", [super superclass]);
}
return self;
}
@end
// TODO: ----------------- main -----------------
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *stu = [[Student alloc] init];
}
return 0;
}
打印结果应该是:
[self class] = Student
[self superclass] = Person
---------------------------
[super class] = Person
[super superclass] = NSObject
真实打印结果是:
真实打印结果Q:为什么打印结果和想象中有出入?
- 关键在
super
关键字在调用方法时的底层调用流程。
面试题一
1> self
与super
// TODO: ----------------- Person类 -----------------
@interface Person : NSObject
- (void)run;
@end
@implementation Person
- (void)run {
NSLog(@"%s", __func__);
}
@end
// TODO: ----------------- Student类 -----------------
@interface Student : Person
@end
@implementation Student
- (void)run {
[super run];
NSLog(@"Student - run");
}
@end
通过xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Student.m
将Student.m
转化为C++代码查看其底层实现:
即[super run];
语句转化为C++代码:
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){
(id)self,
(id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("run"));
// 简化代码
objc_msgSendSuper((__rw_objc_super){
self,
class_getSuperclass(objc_getClass("Student"))
}, @selector(run));
// 继续简化代码
struct __rw_objc_super arg = {
self,
class_getSuperclass(objc_getClass("Student"))
};
objc_msgSendSuper(arg, @selector(run));
由以上代码可知:
-
[super run];
底层实现是objc_msgSendSuper
函数; -
objc_msgSendSuper
函数传入两个参数:__rw_objc_super
结构体与@selector(run)
方法名; -
__rw_objc_super
结构体传入两个参数:self
与class_getSuperclass(objc_getClass("Student"))
即Student
的父类Person
OC源码中搜索objc_msgSendSuper
:
由OC源码可知:
objc_msgSendSuper
函数传入的第一个参数为objc_super
结构体;
objc_super
结构体传入两个参数:
receiver消息接收者为self;
superclass决定从哪一个类开始查找方法的实现。
由上图可知:
super
调用方法:消息接收者仍然是子类对象self
,从父类开始查找方法的实现。
2> class
与superclass
class
与superclass
的底层实现如下图所示:
class
方法的内部实现是根据消息接收者返回其对应的类对象。而class
方法是在基类NSObject
类中实现的。
则[self class];
和[super class];
的消息接收者都是本类Student
。
区别为:[self class];
从本类类对象开始查找方法,[super class];
从父类类对象开始查找方法。
superclass
方法的内部实现是根据消息接收者返回其对应的父类的类对象。
则[self superclass];
和[super superclass];
的消息接收者都是父类Person
。
区别为:[self superclass];
从父类类对象开始查找方法,[super superclass];
从父类的父类类对象开始查找方法。
2. 面试题二
isa、superclass指向图根据isa
、superclass
指向图,得到以下结论:
由以上源码可知:
结论一:isMemberOfClass
判断左边类型 是否等于 右边类型;
结论二:isKindOfClass
判断左边类型 是否等于 右边类型或其子类;
结论三:左边(实例对象) 对应 右边(类对象);
结论四:左边(类对象) 对应 右边(元类对象);
结论五:基类的元类的superclass
指向基类。
// TODO: ----------------- Person类 -----------------
@interface Person : NSObject
@end
@implementation Person
@end
// TODO: ----------------- main -----------------
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
NSLog(@"%d", [person isMemberOfClass:[Person class]]); // 1,结论一、三
NSLog(@"%d", [person isMemberOfClass:[NSObject class]]); // 0,结论一、三
NSLog(@"%d", [person isKindOfClass:[Person class]]); // 1,结论二、三
NSLog(@"%d", [person isKindOfClass:[NSObject class]]); // 1,结论二、三
NSLog(@"%d", [[Person class] isMemberOfClass:[NSObject class]]); // 0,结论一、四
NSLog(@"%d", [[NSObject class] isMemberOfClass:[NSObject class]]); // 0,结论一、四
NSLog(@"%d", [[Person class] isKindOfClass:[NSObject class]]); // 1,结论二、四、五
NSLog(@"%d", [[NSObject class] isKindOfClass:[NSObject class]]); // 1,结论二、四、五
}
return 0;
}
3. 面试题三
// TODO: ----------------- Person类 -----------------
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
- (void)print;
@end
@implementation Person
- (void)print {
NSLog(@"my name is %@", self.name);
}
@end
// TODO: ----------------- ViewController类 -----------------
QQ20190222-152850@2x.png
QQ20190222-153008@2x.png
super的本质、栈空间、消息机制、访问成员变量的本质
网友评论