8.3.2 重载dealloc方法和关键字super
既然知道如何重载方法后,那么返回代码清单8-5学习释放origin所占内存的更好办法。setOrigin:方法现在为自己的XYPoint origin对象分配内存,并且你负责释放它的内存。代码清单8-6中使用的方法就是使用以下语句让main释放该内存:
[[myRect origin]realease];
所以不必担心释放所有单独的类成员,可以重载继承的dealloc方法(它是从NSObject继承的)并在其中释放origin的内存。
注意你不重载release方法,而是重载dealloc方法。在后续章节中你将了解,release有时释放对象使用的内存,有时却不。只有在其他人引用某个对象时,release才释放该对象所占用的内存。这通过调用该对象的dealloc方法来完成,实际上是由dealloc来释放内存。
如果决定重载dealloc方法,必须确保不仅要释放自己的实例变量所占用的内存,而且释放继承的变量所占的内存。
为此,需要利用关键字super,它引用消息接收者的父类。可以向super传递消息来执行重载的方法。这就是此关键字最常见的用途。所以,在方法内部使用消息表达式
[super release];
时,调用定义在父类中的(或是父类继承的)release方法。此方法是对消息的接收者调用的,换言之,是对self调用。
因此,为Rectangle类重载dealloc方法的策略是,首先要释放origin所占的内存,然后调用父类的dealloc方法完成这项任务。这就释放了Rectangle对象自身所占的内存。下面是这个新方法:
-(void)dealloc
{
if(origin)
[origin release];
[super dealloc];
}
定义dealloc方法没有返回值。通过查看头文件<NSObject.h>可以了解这点。在dealloc方法中,进行一项测试来查看释放origin之前它是否非零。很可能从未设置矩形的原点,这种情况下,它拥有默认值零。然后调用父类的dealloc方法,如果没有重载,则Rectangle类将继承此方法。
应该指出,还可以如下更简单地编写这个dealloc方法:
(void)dealloc
{
[origin release];
[super dealloc];
}
因为可以向nil对象发送消息。此外,要注意此时是release原点,而不是dealloc它。如果没有其他人使用原点,release就会在原点调用dealloc方法来释放它的空间。
有了这个新方法,现在只需释放分配了内存的矩形,而无须担心其中包含的XYPoint对象。代码清单8-5所示的两条release消息:
[myRect release];
[myPoint release];
足以释放在程序中分配内存的所有对象,包括setOrigin所创建的XYPoint对象。
还有一个问题:如果在程序的执行期间将单个Rectangle对象的原点设置为不同的值,那么在分配和指定新的原点之前,必须释放旧原点所占的内存。例如,在下面的代码序列中:
myRect.Origin=startPoint;
……
myRect.Origin=endPoint;
……
[startPoint release];
[endPoint release];
[myRect release];
XYPoint startPoint的副本保存在myRect的成员origin中,它从未被释放,这是因为它被存储在其中的第二个原点(endPoint)所重写了。根据新的release方法,只当释放矩形自身时才能释放原点。
需要确保在设置矩形的新原点之前释放旧的原点。这可以在setOrigin:方法中如下进行处理:
-(void)setOrigin:(XYPoint*)pt
{
if(origin)
[origin release];
origin=[[XYPoint alloc]init];
[origin setX:pt.x andY:pt.y];
}
幸运的是,在合成存取器方法时,还可以让编译器自动处理这类问题。