内存管理个人总结
无论编写任何程序,都需要确保能够有效和高效地管理资源。程序内存就是这些资源中的一种。在Objective-C程序中,必须确保所创建的对象,在不再需要它们的时候被销毁。
注意:iOS不提供垃圾回收机制。
一般规则:
只能释放或自动释放自己所拥有的对象
一般以alloc ,new, copy创建的对象都具有所有权,或者如果向一个对象发送了一条retain消息,也会获得该对象的所有权。
此时需要用release进行释放或者调用自动释放池autorelease;自动释放池的调用是系统来完成的。
需要将接收到的对象存储为某个实例变量的属性,您必须保留或复制该对象。
释放实例:
UIView *view 1= [[UIView alloc]init];
Self.view = view1;
[view1 release];
不需要释放的例子:
UIButton*button=[UIButton buttonWithType:UIButtonTypeRoundedRect];
由于调用的类方法没有使用alloc,new,copy等所以它自动释放,不需要手动释放。
调用自动释放池的例子:
-(NSArray *)rearray
{
NSArray *array = [[NSArray alloc]initWithObjects:@”1”,@”2”,nil];
return [array autorelease];
}
这个方法既没有破坏内存规则,又避免了接收值的使用者不知道何时销毁对象的现象。如果直接return array会造成内存泄露,如果先释放在返回的话会返回无效对象([array release];return array;)这也是错误的。当然如果直接调用类方法就可以这样做:
-(NSArray *)rearray
{
NSArray *array = [NSArray arrayWithObjects:@”1”,@”2”,nil];
return array;
}
在 Objective-C 程序中,对象会被创建和销毁。为了确保应用程序不会使用不必要的内存,对象应该在不需要它们的时候被销毁。当然,在需要对象时保证它们不被销毁也很重要。
任何对象都可能拥有一个或多个所有者。只要一个对象至少还拥有一个所有者,它就会继续存在。如果一个对象没有所有者,则运行时系统会自动销毁它。
可以使用retain来获得一个对象的所有权。
保留计数(retainCount)
在调用retain方法后通过引用计数—通常被称为“保留计数”—实现的。每个对象都有一个保留计数。
创建一个对象时,该对象的保留计数为1。
向一个对象发送retain消息时,该对象的保留计数加1。
向一个对象发送release消息时,该对象的保留计数减1。
向一个对象发送autorelease消息时,该对象的保留计数会在将来的某个阶段减1。
如果一个对象的保留计数被减为0,该对象就会被回收.(直接调用dealloc进行释放)。
当显式地查询对象的保留计数是多少。由于添加了保护机制,当保留计数被减为0时,控制台输出仍然为1因此会造成误导。
共享对象的有效性
Cocoa的所有权策略规定,被接收的对象通常应该在整个调用方法的作用域内保持有效。此外,还可以返回从当前作用域接收到的对象,而不必担心它被释放。对象的getter方法返回一个缓存的实例变量或者一个计算值,这对您的应用程序来说无关紧要。重要的是,对象会在您需要它的这段期间保持有效。
这一规则偶尔也有一些例外情况,主要可以总结为以下两类。
当对象从一个基本的集合类中被删除的时候。
heisenObject = [array objectAtIndex:n];
[array removeObjectAtIndex:n];
// heisenObject could now be invalid.
当对象从一个基本的集合类中被删除时,它会收到一条release(不是autorelease)消息。如果该集合是这个被删除对象的唯一所有者,则被删除的对象(例子中的heisenObject)将被立即回收。
当一个“父对象”被回收的时候。
id parent = <#create a parent object#>;
// ...
heisenObject = [parent child] ;
[parent release]; // Or, for example: self.parent = nil;
// heisenObject could now be invalid.
在某些情况下,您通过另外一个对象得到某个对象,然后直接或间接地释放父对象。如果释放父对象会使其被回收,而且父对象是子对象的唯一所有者,那么子对象(例子中的heisenObject)将同时被回收(假设它在父对象的dealloc方法中收到一条release而非autorelease消息)。
为了防止这些情况发生,您要在接收heisenObject后保留该对象,并
在使用完该对象后对其进行释放,例如:
heisenObject = [[array objectAtIndex:n] retain];
[array removeObjectAtIndex:n];
// use heisenObject.
[heisenObject release];
如果在您的类中有实例变量对象,您必须实现一个dealloc方法来释放它们,然后调用超类的dealloc实现。
重要:决不要直接调用另一个对象的dealloc方法。
保留循环
在某些情况下,两个对象之间可能会出现循环引用的情况,也就是说,每一个对象都包含一个实例变量引用对方对象。例如,考虑一个文本程序,程序中对象间的关系如图1所示。“文档(Document)”对象为文档中的每个页面创建一个“页(Page)”对象。每个Page对象具有一个实例变量,用来跟踪该页所在的文档。如果Document对象保留了Page对象, 同时Page对象也保留Document对象,则这两个对象都永远不会被释放。只有Page对象被释放,Document的引用计数才能变为0,而只有Document对象被回收,Page对象才能被释放。