self = [super init]的理解

1,005 阅读3分钟

Objective-C的推荐init方法写法如下:

- (id) init
{
    if(self = [super init])
    {
        //为子类增加属性进行初始化
    }
    return self;
}

大家都这么写,也都知道这么写,我这里提出几个问题:

  1. [super init] 到底做了什么?
  2. 为什么把**[super init]**的值赋值给self?
  3. super作为消息接受者的实质是什么

问题1:

从代码层面来看,[super init]调用父类的初始化方法,就这么简单,那父类的初始化方法又执行了什么? 首先,我们知道对象继承的概念,一个子类从父类继承,那么也要实现父类的所有功能,这就是is-a的关系,比如狗是哺乳动物,那么狗必须具有哺乳动物的特征和功能,所以在子类的初始化方法中,必须首先调用父类的初始化方法,以实现父类相关资源的初始化.总结一句话:[super init]面向对象的体现,先利用父类的init方法为子类实例的父类部分属性初始化

先不着急回答第二个问题,我们看看[super init]执行会有什么结果

第一种: [super init]初始化成功,这是我们常见的正常情况

第二种: [super init]初始化失败,我们都知道oc创建对象的两步,alloc开辟内存空间,init初始化对象,但init还有另外一个作用,在初始化失败的时候,init方法会返回一个nil,那么看最上面的代码,如果self为nil,那么if(self)里面的内容将不被执行,保证了程序的健壮性

第三种:self = [super init]执行后,内存指向了不相关的地址

对于第一,第二种结果没什么疑问,我们来看看第三种,很多人一脸雾水,没明白什么意思,看下面代码

假设有父类Father与子类Son

Father的init方法如下:

- (instancetype)init
{
    id tmp = self;
    self = [Father alloc];
    [tmp release];
    return  self;
}

Son的init方法如下:

- (instancetype)init
{
    if (self = [super init]) {

        NSLog(@"son init %@", self.class);

    }
    return  self;
}

这时编译能通过,但当Son的实例使用Son扩充的属性时,就会出现一个运行时错误.错误的原因在于Father的init方法用[Father alloc]重新获得了一块仅仅适合存放Father实例的空间.而Son的init方法以为这是块适合存放Son的空间,当试图读写Son的扩充属性时就会产生运行时错误

因此,当init方法需要重新alloc一块空间时,正确的写法如下:

- (id) init
{
    id tmp = self;         
    self = [[self class] alloc];          
    [tmp release];
    //other staffs
    return self;
}

注意第4行 ,[self class]将获得self指向的实例对应的类实例,本例中便是Son。这样Father的任何子类的init方法都能保证安全了。

通过上面的例子,咱们来回答问题2,简单来说就是为了防止父类的初始化方法release掉了self指向的空间并重新alloc了一块空间,这样的话,[super init]可能alloc失败,这时就不再执行if中的语句,一句话概况:if(self = [super init])这是一种常用的建议写法,赋值并测试nil是为了防止超类在初始化过程中发生改变,返回了不同的对象

问题3比较简单: super并不是真正的指针,[super message]的实质是由self来接受父类的message。 需要注意的是,[super message]中,message方法出现的self为[super message]语境中的self,即子类实例。