最近小编所在公司招 iOS 开发职位,小编也出了几道面试题考察下候选人的 iOS 开发水平,其中有一道题如下:
@implementation Student : Person
- (instancetype)init
{
self = [super init];
if (self) {
id obj1 = [self class];
id obj2 = [super class];
NSLog(@"%@",obj1);
NSLog(@"%@",obj2);
}
return self;
}
@end
大部分候选人回答的 [self class ]输出 Student , [Super class]输出 Person, 只有少部分候选人回答都是输出 Student ,当然至于为什么输出结果都是 Student, 很少有能回答出来的.
接下来小编通过将Student.m 转换成 Cpp 文件,带大家一块去看看 [self class] 和 [super class] 背后究竟做了那些事情.
通过终端讲Student.m 转成Student.cpp 文件
image.png
2 找到 Student 的 init 方法 分析代码
static instancetype _I_Student_init(Student * self, SEL _cmd) {
self = ((Student *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("init"));
if (self) {
id obj1 = ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"));
id obj2 = ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("class"));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_hj_pwgsq9614nb0vq4zd315tcx80000gn_T_Student_e7cbc1_mi_0,obj1);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_hj_pwgsq9614nb0vq4zd315tcx80000gn_T_Student_e7cbc1_mi_1,obj2);
}
return self;
}
由此可见 当我们调用[self class] 时候实际上编译器最终给我们转成
objc_msgSend(self,@selector(class)) , 消息的接收者是当前所在类的实例对象 , 这个时候就会去 self 所在类 Student 去查找 class 方法 ,
如果当前类 Student 没有 class 会向Student 父类 Person 类找 class 方法,
如果 Person 类也没有找到 class 方法,最终会找到最顶级父类 NSObject 的 class 方法,
最终找到 NSObject 的 class 方法 ,并调用了object_getClass(self) ,由于消息接收者是 self 当前类实例对象,
所以最终 [self class]输出 Student
那么为什么明明调用了 super 这个关键字 返回的[super class] 还是 Student 呢 ?
通过上边代码可知 , [super class] 最终编译器转化成了 objc_msgSendSuper(struct objc_super *,SEL) ,其中
/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver;
/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
/* super_class is the first class to search */
};
#endif
objc_super 是一个结构体,内部有一个 receiver 实例,和一 个 Class super_class,指向了当前类的父类 Class ,通过这个父类可以直接的从父类里边开始查找方法,由于消息接收者还是当前类的实例对象 self, 最终如果在父类中没有找到class 这个方法,会在 Person 类的父类 NSObject 中去查找 class 方法,由于 NSObject 的 class 方法,调用的是 object_getClass(self) ,所以最终消息接收者为 student 实例对象,所以返回的还是 Student .
当然我们知道了[ super class] 最终编译后代码样子,也可以自己手动去调用 objc_msgSendSuper 方法
- (instancetype)init
{
self = [super init];
if (self) {
id obj1 = [self class];
id obj2 = [super class];
NSLog(@"%@",obj1);
NSLog(@"%@",obj2);
struct objc_super1 {
__unsafe_unretained id receiver;
Class superClass;
};
struct objc_super1 obj_super = {self,class_getSuperclass(object_getClass(self))};
id obj3 = objc_msgSendSuper(&obj_super,@selector(class));
}
return self;
}
obj3输出的结果和直接调用 [super class]结果是一模一样的,刚才我们假设的情况都是 super Person 没有 class 方法,如果 Person 重写了 class 方法呢,将会怎么样?
image.png当我们在 Student init 方法中调用 [super class] 时候 ,它首先会到 Person 类中查找 class 方法 ,当它发现了 Person 实现了 class 方法,就会调用[person class] 方法, object_getClass 这个时候 object_getClass(self), 这个 self 是 Student 的实例对象,就是消息接收者,所以即使重写了 Person 的 class 方法 ,依然返回的还是 Student ,除非来个极端的 把 class 方法 实现 返回个 nil , 这样最终调用[super class]结果才会返回nil
KVO巧妙的利用了子类 重写了 class 方法 ,让我们误以为 [person class] 还是当前类 Person ,而不是动态创建的子类 NSKVONotifily_Person 这个子类 , 就是让子类返回了父类的 Class ,所以调用 [person class] 返回的还是 Person 从而对开发者隐藏了 NSKVONotifily_Person子类的存在
- (Class)class {
return class_getSuperclass(object_getClass(self));
}
好了,我是大兵布莱恩特,欢迎加入博主技术交流群,iOS 开发交流群
QQ20180712-0.png
网友评论
非常感谢~~~