您的位置:首页 > 其它

self和super到底怎么用?

2015-08-20 15:28 363 查看
开发过程中遇到一个问题.

问题简化描述如下:

有一个UIView的子类(CTestLevel),实现了init方法和initWithFrame方法,只调用了CTestLevel的init,init中只调用了[super init] , 可为什么当前类的initWithFrame被调用了?不应该是调用父类吗?怎么调用了子类的initWithFrame?

如下:

初始化一个对象

CTestLevel  *testObj = [CTestLevel  alloc] init];


@implementation CTestLevel

- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
NSLog(@"initWithFrame");
}

return self;
}

-(instancetype)init
{
self = [super init];
if (self)
{
NSLog(@"init");
}

return self;
}
@end


2.设置断点,查看调用关系



3 查看堆栈



查看调用关系和堆栈,

testObjc调用了init方法,然后调用了UIView的init的方法,这没有问题,

因为CTestLevel的init方法调用了[super init];

堆栈中最上层为什么又调用了CTestLevel 的initWithFrame方法呢?

initWithFrame是UIView的指定初始化方法,按道理应该调用UIView的initWithFrame方法啊,为什么调用了CTestLevel的initWithFrame的初始化方法?

因为子类重写了initWithFrame,所以调用子类的initWithFrame?如果不重写就调用UIView的initWithFrame?

super self 都是什么意思?

这都是怎么一个逻辑,具体是怎么实现的?

先说明Objective-C的类图结构:



其中实心箭头表示继承关系,CTestLevel继承自UIView,UIView继承自UIResponder, …一直继承到NSObject.NSObject的父类为nil, 所有类变量到最终的父类都是nil.

虚线箭头表示所属关系:testObj属于CTestLevel类的对象,CTestLevel类属于CTestLevel元类的对象。所有的元类对象属于NSObject元类对象,NSObject元类对象属于自身.

代码说明Objective-C类的结构

Objective-C中所有的东西都是对象,即使是类,它也是一种对象。

testObj对象的如下表示:

/// Represents an instance of a class.
struct objc_object {
Class isa  OBJC_ISA_AVAILABILITY;
};


objc_object 表示一个对象,其中的isa指向CTestLevel类

Class的结构体定义如下:

typedef struct objc_class *Class;

struct objc_class {
Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
Class super_class                                        OBJC2_UNAVAILABLE;
const char *name                                         OBJC2_UNAVAILABLE;
long version                                             OBJC2_UNAVAILABLE;
long info                                                OBJC2_UNAVAILABLE;
long instance_size                                       OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;


其中isa指向CTestLevel元类,super_class指向其父类UIView,ivars存放变量,methodLists方法列表,cache缓存方法,protocols类所遵循的协议。

self和super的含义,在Objective-C中self表示当前对象(就是这个testObj对象),类似于C++里面的this指针;

super其实是一个编译器指示符,在Runtime运行期这个关键词是不存在的。只是辅助编译器做了一些处理罢了,类似语法糖的东西。

如何向对象发消息

testObj对象可以调用(init,initWithFrame, setHidden等方法),具体是怎么做到的?

例如:

当[testObj init]这样发送消息的时候,会转化为 id objc_msgSend(id theReceiver, SEL theSelector, …)来调用。其中theReceiver表示testObj对象,theSelector表示init方法. …表示参数,这里参数为空。

简写成如下这样:obj_msgSend(testObj,init,”“)

如果是调用哪个initWithFrame的话,大体如下:

obj_msg_Send(testObj,@Selector(initWithFrame),frame)

那调用[super init]的时候,没有testObj这个对象了?怎么办?

[super init]会转化成如下的方法

id objc_msgSendSuper(struct objc_super *super, SEL op, …)

一个objc_super,它具体是什么呢?结构体定义如下:

struct objc_super {
id receiver;
Class superClass;
};


里面也有一个receiver,这个receiver就是消息的接受者,这里是self,就是这个testObj对象!

superClass: self的父类,也就是UIView.

op:表示方法;这里就是init.

一句话:去父类里面找init方法,然后给testObj发消息。对,用当前对象,调用父类的方法。

self和super的区别,就是去哪里找方法的问题,接受消息的对象还是那个对象。

举个例子说明:

类的继承关系:



CTestLevel_3继承自CTestLevel_2,CTestLevel_2继承自CTestLevel_1,CTestLevel_1继承自UIView.

testObj是一个CTestLevel_3类型的对象。

CTestLevel_3实现了一个testLevel3方法,方法直接调用 : [super testLevel2];

CTestLevel_2实现了一个testLevel2方法,方法打印输出:NSStringFromClass([self class]);

因为testObj是一个CTestLevel_3的对象类型,所以输出结果[self class]是一个CTestLevel_3类型。等价于给CTestLevel_3类型的对象,发送父类的testLevel2消息。

经常遇到下面的问题:

NSLog(@"%@\n",self); //CTestLevel_3
NSLog(@"%@\n",[self class]);//CTestLevel_3
NSLog(@"%@\n",[self superclass]);//CTestLevel_2
NSLog(@"%@\n",[[self class] superclass]);//CTestLevel2 class
NSLog(@"%@\n",[super class]);//CTestLevel3
NSLog(@"%@\n",[super superclass]);//CTestLevel2


完。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: