原文:http://www.cnblogs.com/margincc/archive/2010/11/25/2095067.html
obj中创建新对象有两种方式:[classname new]和[[classname alloc] init]。两种方法等价,Cocoa惯例是使用alloc和init。
一、分配对象:
allocation是一个新对象诞生过程,从OS获得一块内存并指定为存放对象的实例变量的位置。同时alloc方法还将这块内存区域全部初始化为0。BOOL初始化为NO,int初始化为0,float初始化为0.0,指针初始化为nil。
然后init初始化之后才能使用,C++和Java中使用构造函数在单次操作中执行对象的分配和初始化,Objective-C将两个操作分开。
示例1
Car * car = [[ Car alloc ] init ];-( id ) init{if ( self =[ super init ]){engine =[ Engine new ];tires [ 0 ]=[ Tire new ];}}
-(id)init返回值,id可表不同的对象。init可以接受参数并可能判断返回另外一个类的对象可能更合适。比如像NSString和NSArray这样的类实际上只是大量专用类的虚假表象。
正因为
init可以接受参数并可能判断返回另外一个类的对象可能更合适。所以对于一个很长的字符串或一个阿拉伯字符串生成一个新的字符串的情况。
if(self=[super init]) 首先的是[super init],使超类完成自己的初始化工作。使超类执行所需任何操作,以便对象能响应消息并处理保留计数器。
实例变量所在的内存位置到隐藏的self的距离是固定的,如果init方法返回一个新对象,则需要更新self,以便之后的实例变量的引用能映射到正确的内存位置,self=[super init]赋值就是这个作用,只影响init方法中self的值,不影响init范围以外的内容。初始化一个对象出错时,返回nil。if(self=[super init])典型的C风格,一般不采用self=[super init] if(self)...
在car的初始化中给实例变量赋值并创建car所需要的engine和tires对象。可以一次创建所有所需要的对象,使得Car类可以[[Car alloc] init]之后可以立即使用;也可以先为engine对象和tire对象预留位置,等调用者需要的时候再创建对象,惰性求值(lazy evaluation)。
这里涉及了几个问题,
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,即子类实例。
二、便利初始化函数
为了减少工作麻烦,很多对象有多个init开头的方法。NSString类为例子:
NSString *emptyString = [[NSString alloc] init];
NSString *str = [[NSString alloc] initWithFormat:@"%d or %d",2,33];
NSString * str = [[ NSString alloc ] initWithContentsOfFile :@ "/tmp/1.txt" ];
Xcode提供的自动匹配功能非常使用,输入init按esc键后会显示所有的可以匹配的函数。
所有使用alloc,copy,new方法创建的对象,只用完成之后都需要释放。[str release];
NSString *str = [NSString stringWithFormat:@"%.1f",20.0]; str这里是可以自动释放的,当自动释放池销毁时,该字符串对象也被清理。
一般在main函数首先创建自动释放池,为自动释放的对象在等待自动释放池被销毁时提供容身之所:
NSAutoreleasePool * pool ;pool = [[ NSAutoreleasePool alloc ] init ];
在程序结束时,pool释放,并向池中的所有对象发送release消息:
[pool release];
三、指定初始函数
我们可以自己编写方便的便利初始化函数,并且可以编写多个。但必须指定某个初始化方法为指定初始化函数,该类的所有初始化方法使用指定初始化函数执行初始化操作。子类使用其超类的指定初始化函数实现超类的初始化。如果构造了一个初始化函数,需要在自己的指定初始化函数中调用超类的指定初始化函数。如果不指定初始化函数,那么该类子类可能需要重写所有的它的初始化函数。
四、潜藏的陷阱
假设有父类AObj与子类BObj。
当AObj的init方法如下:
- ( id ) init {id tmp = self ;self = [ AObj alloc ];[ tmp release ];//other staffsreturn 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;}
注意第4行,[self class]将获得self指向的实例对应的类实例,本例中便是BObj。这样AObj的任何子类的init方法都能保证安全了。
下面继续介绍一下self的用法
self表示当前对象(自己),这个关键字多用于对成员变量和成员方法的调用中使用。你可以理解为,它是一个类名反身代词,这个类名指的就是类自己。
进入正题, 我们经常会在官方文档里看到这样的代码:
MyClass.h
@interface MyClass : NSObject{MyObject * myObject ;}@property ( nonatomic , retain ) MyObject * myObject ;@end
MyClass.m
@synthesize myObject ;-( id ) init {if ( self = [ super init ]){MyObject * aMyObject = [[ MyObject alloc ] init ];self . myObject = aMyObject ;[ aMyObject release ];}return self ;}
有人就问, 为什么要这么复杂的赋值? 为什么要加self. ? 直接写成self.myObject = [[MyObject alloc] init];不是也没有错么? 不加self有时好像也是正常的?现在我们来看看内存管理的内容:
先看间接赋值的:
A.加self.
MyObject * aMyObject = [[ MyObject alloc ] init ]; //aMyObject retainCount = 1;self.myObject = aMyObject; //myObject retainCount = 2;[aMyObject release];//myObject retainCount = 1;
B. 不加self.
MyObject * aMyObject = [[ MyObject alloc ] init ]; //aMyObject retainCount = 1;myObject = aMyObject; //myObject retainCount = 1;[aMyObject release];//对象己经被释放
再看直接赋值的:
C.加self.
self.myObject = [[MyObject alloc] init]; //myObject retainCount = 2;
D. 不加self.
myObject = [[MyObject alloc] init]; //myObject retainCount = 1;
现在是不是有点晕, 我们先来把代码改一下, 官方的一种常见写法:
MyClass.h
@interface MyClass : NSObject {MyObject * _myObject ;}@property ( nonatomic , retain ) MyObject * myObject ;@end
MyClass.m
@synthesize myObject = _myObject;
OK, 你现在再试下, 如果你用self._myObject = aMyObject; 或者 myObject = aMyObject; 你会得到一个错误, 为什么呢, 这里就是和Obj-c的存取方法有关了. 说白了很简单 , 大家都知道, @property (nonatomic, retain) MyObject *myObject; 是为一个属性设置存取方法, 只是平时我们用的方法名和属性名是一样的,现在你把它写成不同的名字, 就会很清楚了. _myObject是属性本身, myObject是存取方法名.
关于存取方法的更多内容请参考《
@property和@synthesize》