`
stephen830
  • 浏览: 2965262 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

[Obj-C笔记] "self = [super init]"

 
阅读更多

 

[Obj-C笔记] "self = [super init]"的解释与潜藏bug

 

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

 

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

 

这里涉及了几个问题,

 

1. [super init]的作用:

 

面向对象的体现,先利用父类的init方法为子类实例的父类部分属性初始化。

 

2. self 为什么要赋值为[super init]:

 

简单来说是为了防止父类的初始化方法release掉了self指向的空间并重新alloc了一块空间。这时的话,[super init]可能alloc失败,这时就不再执行if中的语句。

 

3. super作为消息接受者的实质:

 

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

 

 

 

潜藏的bug:

 

假设有父类AObj与子类BObj。

 

当AObj的init方法如下:

 

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

 

BObj的init方法如下:

 

- (id) init
{
    if(self = [super init])
    {
        //other staffs
    }
    return self;
}

 

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

 

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

 

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

 

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

 


 


 


 


 

if( self = [super init] )这是一种通常的建议写法,赋值并测零只是为了防止超类在初始化过程中发生改变,返回了不同的对象

 

 

 

 

为什么一定要  super alloc ?

 

 

 

众所周知,Objective-C是一门面向对象的语言,一般情况下,我们在Objective-C中定义一个类时,总要提供一个初始化方法,一般大家都是这样写的:

 

1 - (MyClass *)init

 

2 {

 

3     self = [super init]; 

 

4     if (self) {

 

5          //执行一些资源、变量的初始化工作

 

6     }

 

7         return self;

 

8 }

 

这样一段简单的代码,却有很多可以思考的问题:

 

1、为什么要通过[super init]来调用父类的初始化方法,父类的初始化方法里又执行了什么东西?

 

      首先,我们知道对象继承的概念,一个子类从父类继承,那么也要实现父类的所有功能,这 就是is-a的关系,比如说狗是哺乳动物,那么狗必定具有哺乳动物的特征和功能。所以在子类的初始化方法中,必须首先调用父类的初始化方法,以实现父类相 关资源的初始化。例如我们在初始化狗这一对象时,必须先初始化哺乳动物这一对象,并把结果赋予狗,以使狗满足属于哺乳动物这一特征。

 

     典型的,在iOS下,所有的类都继承于NSObject,而NSObject的init方法很简单,就是return self。当父类的初始化完成之后,即self不为nil的情况下,就可以开始做子类的初始化了。

 

2、是否一定要提供初始化方法,是否一定要使用init作为初始化方法?

 

     我们在Objective-C中创建一个对象通常使用

 

1 MyClass *newclass = [[MyClass alloc] init];

     或者

 

 
MyClass *newclass = [Myclass new];  

 

new方法是NSObject对象的一个静态方法,根据apple的文档,该方法实际上就是alloc和init方法的组合,实际上二者是一样的,但 apple还是推荐我们使用第一种方法,为什么呢?因为使用第一种方法,你可以使用自己定义的init方法来做一些初始化(用自己写的init*****方法), 当然,如果子类没有提供 init方法,自然调用的就是父类的init方法了。所以说,从安全性的角度来收,作为开发者我们在对象使用之前是一定要对对象进行初始化的,因此在定义 类的时候一定要提供初始化方法。但是否一定要使用init作为方法名呢?答案是不一定。使用init作为方法名只是你重写了NSObject的init方 法而已,如果你自己重新定义一个初始化方法,也是完全可以的,只要你在使用的时候记得调用新定义的初始化方法就可以了。

 

但是,这种方法从设计角度来看我觉得是不可取的。在可复用性方面会比较差,如果确有必要定义一些接受不同参数的初始化方法,我的建议是,先定义一个init的公用方法,再到其他方法中调用它,如:

 

01 - (id)init   init的公用方法
02 {
03     self = [super init]; 
04     if (self) {
05            
06     }
07     return self;
08 }
09  
10 - (id)initWithString:(NSString *)aString
11 {
12     [self init];
13     self.name = aString;  
14 }
15   
16 - (id)initWithImage:(UIImage *)aImage
17 {
18     [self init];
19     self.image = aImage;  
20 }

 

补充:

 

在面向对象编程中,如果编写一个类而没有包含构造函数,这个类仍能编译并且完全可以正常使用。如果类没有提供显式的构造函数,编译器会提供一个默认的构造 函数给你。除了创建对象本身,默认构造函数的唯一工作就是调用其超类的构造函数。在很多情况下,这个超类是语言框架的一部分,如java中的 Object类,objective-c 中的NSObject类。

 

不论是何种情况,在类中至少包含一个构造函数是一种很好的编程实践,如果类中有属性,好的实践往往是初始化这些属性。

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics