您的位置:首页 > 移动开发 > IOS开发

iOS学习——self和super关键字的解析

2016-05-12 22:31 459 查看
以一道面试题开篇:

@implementation Son : Father
- (id)init
{
self = [super init];
if (self)
{
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end

这段语句的打印结果是什么?
按照Java语言的经验来看,答案应该是Son Father,但是实际编译运行后打印结果却是:Son Son。

下面我们来分析一下原因。

self和super是Objective-C的两个保留字,self是类的隐藏的参数,它指向当前调用方法的对象,另一个隐藏参数是_cmd,代表当前类方法的selector。而super并不是隐藏的参数,它仅仅是一个“编译器指示符”,它和self指向的是相同的消息接收者。对于上述代码而言,[self class]和[super class]的消息接收者都是Son这个对象,不同的是,super关键字告诉编译器当调用class方法时,要去调用父类中的方法。

对于其中的底层原理分析如下。

我们知道,OC语言会把所有的方法调用转换成消息发送函数:当调用[self class]方法时,编译器会转换成如下代码

id objc_msgSend(id theReceiver, SEL @selector(class)); 第一个参数是消息接收者,第二个参数是调用的具体类方法的selector,对于[self class]方法而言,theReceiver是self,SEL是@selector(class);
而当调用[super class]时,会生成objc_msgSendSuper函数:

id objc_msgSendSuper(struct objc_super *super, SEL op, ...);
第一个参数是objc_super的结构体,第二个参数类似上面的selector。先看一下objc_super这个结构体是什么东西:
struct objc_super{
id receiver;
Class superClass;
};
该结构体包含了两个成员:receiver和superClass。receiver类似objc_msgSend函数中的第一个参数,superClass记录了该类的父类。当调用[super class]时,编译器开始做这几件事:
1、构建objc_super的结构体。此时,这个结构体的第一个成员变量receiver和self相同,而第二个成员变量superClass指的是Son的父类Father;

2、调用objc_msgSendSuper方法,将objc_super结构体和class的SEL传递过去。然后函数做如下操作:从objc_super结构体指向的superClass的方法列表开始查找class的selector,找到以后内部使用函数objc_msgSend(objc_super->receiver,@selector(class))调用,此时与[self class]调用情况相同。

下面分析一下为什么要用self = [super init];

调用[super init]的目的是为了使父类完成他们自己的初始化工作,当我们想要用到某个子类的实例,那么我们必将会用到该子类继承自其父类的某些属性(如:UIButton继承与UIView的某些属性),如果由于某些原因[super init]在初始化它的属性时没有成功,在父类的init方法中返回了一个nil,那么我们得到的button实例的self也是一个nil。此时if(self=[super init])这行语句就不会再执行,这样做防止了程序行为的不可预测和可能导致程序的崩溃。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: