Runtime关于self和super的理解与解释
这个问题出自sunnyxx的一题:
@implementation Son : Father
- (id)init {
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
结果是两个都输出Son。
对于这个问题的解释感觉各个地方都说的太高深,在这里讲一下自己的理解和猜测。
先来复习一下objc_msgSend的流程(伪码):
简单的说就是
1.先在对象自己的类对象中查找对应的方法指针IMP,找到了就执行IMP,并且第一个参数为对象自己self。
2.找不到就去父类对象中查找IMP,找到了就执行IMP,并且第一个参数为对象自己self。
3.最后哪都找不到就进入消息转发。
注意无论最后调用的IMP是自己的还是父类的,传入的self都是对象自己。
id objc_msgSend(id self, SEL op, ...) {
if (!self) return nil;
IMP imp = class_getMethodImplementation(self->isa, SEL op);
imp(self, op, ...); //调用这个函数,伪代码...
}
//查找IMP
IMP class_getMethodImplementation(Class cls, SEL sel) {
if (!cls || !sel) return nil;
IMP imp = lookUpImpOrNil(cls, sel);
if (!imp) return _objc_msgForward; //_objc_msgForward 用于消息转发
return imp;
}
IMP lookUpImpOrNil(Class cls, SEL sel) {
if (!cls->initialize()) {
_class_initialize(cls);
}
Class curClass = cls;
IMP imp = nil;
do { //先查缓存,缓存没有时重建,仍旧没有则向父类查询
if (!curClass) break;
if (!curClass->cache) fill_cache(cls, curClass);
imp = cache_getImp(curClass, sel);
if (imp) break;
} while (curClass = curClass->superclass);
return imp;
}
那么这道题的关键就在于对父类调用方法又是怎样一个流程呢
首先编译后不再是objc_msgSend,而是:
id objc_msgSendSuper(struct objc_super *super,SEL op)
super在编译后会被创建为一个objc_super结构体:
struct objc_super { id receiver; Class class; };
objc_super结构体创建的语句如下:
可以看出来结构体的的receiver成员就是对super发送消息的对象self,结构体的class成员是对象的父类对象
(__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son")}
接下来是对objc_msgSendSuper内部流程的猜测:
1.objc_msgSendSuper从super结构体中获取到父类对象,在父类对象中查找IMP,如果找到了就调用IMP,传入第一个参数为super结构体中的receiver,既调用父类方法对象self。
2.如果在父类对象中查找不到,就去父类的父类对象中找,直到找到IMP,然后调用。
3.哪都找不到进入消息转发。
结合一开始的题目
[self class]流程:
1.在自己的类对象中查找IMP,找不到。
2.在父类对象中也找不到,然后追寻继承链一直找到NSObject,找到了,调用IMP并传入对象自己self为第一个参数,所以返回的结果是对象自己的类对象,也就是Son。[super class]流程:
1.直接在父类对象中查找IMP,找不到。
2.然后追寻继承链一直找到NSObject,找到了,调用IMP并传入调用父类方法的对象为第一个参数(即还是传入self),所以返回的结果是调用父类对象自己的类对象,也就是Son。也就是说两者最后都是调用NSObject的class方法并传入self,返回self的类对象。
拓展
结合这个思路,我们考虑
1 [self init]:
在自己的类对象中查找IMP,找到了,调用IMP并传入对象自己self为第一个参数。
2 [super init]:
直接在父类对象中查找IMP,找到了,调用IMP并传入调用父类方法的对象self为第一个参数。
总结
所以理解的重点就是到底找到的IMP是哪个类的,是自己的类对象中的IMP,还是父类对象中的IMP,还是更上层的某个父类对象中的IMP。
其次就是无论调用的是哪个IMP,对IMP传入的第一个参数对象都是一开始的调用者本身self。这也说明了为什么调用的IMP属于不同的类对象,但是IMP函数中所使用的成员变量永远都是调用者对象中的成员变量,因为虽然IMP储存在不同的类对象中,但是成员变量永远是储存在实例对象中。
PS:若有错误请指教,谢谢。
上一篇: OOA和OOD的合理解释
下一篇: java基数排序算法代码下载