[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]; |
或者
|
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类。
不论是何种情况,在类中至少包含一个构造函数是一种很好的编程实践,如果类中有属性,好的实践往往是初始化这些属性。
相关推荐
obj-c, win 环境下的 makefile 和简单的程序,自己用的
obj-c2.0大全
1. tar zxvf arm-linux-gcc-4.3.2.tgz -C/ 2. 编辑 ~/.bashrc ,加入 export PATH=$PATH:/usr/local/arm/4.3.2/bin 3. 验证安装 [root@xxxxx root]# arm-linux-gcc -v Using built-in specs. Target: arm-none-linux...
1. tar zxvf arm-linux-gcc-4.3.2.tgz -C/ 2. 编辑 ~/.bashrc ,加入 export PATH=$PATH:/usr/local/arm/4.3.2/bin 3. 验证安装 [root@xxxxx root]# arm-linux-gcc -v Using built-in specs. Target: arm-none-linux...
本程序的Makefile分为3类: ... "obj-y += file.o"表示把当前目录下的file.c编进程序里, "obj-y += subdir/"表示要进入subdir这个子目录下去寻找文件来编进程序里,是哪些文件由subdir目录下的Makefile决定。
模型格式转换工具(osgb-obj-ive) 可在osgb、ogb、obj、ive间转换,亲测可用。
revit导出obj功能MIT协议随意使用。.net C#工程 导出 obj +mtl文件 并且较少 原有revit大小(导出信息减少非轻量化)
个人学习objective-c和xcode helloworld 笔记,比较了objective-c 和C++ JAVA的一些区别
Yahoo开源的在线obj-c -> swift工具
OBJ-socket.OBJ-socket.
SVGQuartzRenderer is a basic SVG renderer written in Obj-C that uses CoreGraphics to render SVG files. The goal of SVGQuartzRenderer is to be a simple drop-in SVG renderer that is compatible with the ...
Objective-C 2.0之前需要了解的:关于Obj-C内存管理的规则。
Objective-C+2.0之前需要了解的:关于Obj-C内存管理的规则
C中使用的不同小程序valgrind命令: valgrind --leak-check =满--show-leak-kinds = all --track-origins =是-冗长--log-file = valgrind-out.txt ./executable ...c) OBJ:= $(SRC:$(SRC_DIR)/%。c = $(OB
* $obj = new WaterMask($imgFileName); //实例化对象 * $obj->$waterType = 1; //类型:0为文字水印、1为图片水印 * $obj->$transparent = 45; //水印透明度 * $obj->$waterStr = 'icp.niufee.com'; //水印文字...
SVGQuartzRenderer is a basic SVG renderer written in Obj-C that uses CoreGraphics to render SVG files. The goal of SVGQuartzRenderer is to be a simple drop-in SVG renderer that is compatible with the ...
WebViewJavascriptBridge-用于在UIWebViews/WebViews中Obj-C和JavaScript互通消息的iOS/OSX桥梁
obj文件解析器开源,可重用的Wavefront 3D对象文件(.OBJ)解析器,以javascript编写,并作为NPM软件包自由分发。特征仅支持(.OBJ)文件,请参阅mtl-file-parser以获取mtl文件不区分大小写,灵活的解析器简单的JS...
python库。资源全名:dj-obj-update-0.4.0.tar.gz
前端开源库-obj-flatten对象展平,将嵌套对象转换为展平对象。