objective-C 的内存管理之-自动释放池(autorelease pool)

如果一个对象的生命周期显而易见,很容易就知道什么时候该new一个对象,什么时候不再需要使用,这种情况下,直接用手动的retain和release来判定其生死足矣。但是有些时候,想知道某个对象在什么时候不再使用并不那么容易。如果下面的代码,看上去非常简单:

Sample.h类接口部分

#import
@interface Sample : NSObject {

}

-(NSString*) toString;

@end
Sample.m 类实现部分

#import "Sample.h"

@implementation Sample

-(NSString*) toString
{
NSString* str = [[NSString alloc] initWithFormat:@"This is %@ class",@"Sample"];
return str;
}
@end
在main函数中调用

#import
#import "Sample.h"

int main (int argc, const char * argv[]) {

Sample *s = [Sample new];
NSLog(@"%@",[s toString]);
[s release];

return 0;

}
不知道您是否意识到这段代码有内存泄漏问题,因为Sample.m的toString方法生成了一个NSString类的实例,但是main函数最后只释放了Sample的实例s,却并未释放这个NSString类的字符串实例!

要纠正这个错误,main函数应该改成:

int main (int argc, const char * argv[]) {

Sample *s = [Sample new];
NSString* str = [s toString];
NSLog(@"%@",str);
[str release];//手动释放NSString字符串实例
[s release];

return 0;

}
这种隐藏的错误很容易发生,也容易被忽视。为此obj-c 引用了自动释放池(autorelease pool),每次用xcode创建项目时,可能大家已经注意到了有类似下面的代码模板:

int main (int argc, const char * argv[]) {

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

//insert code here...
NSLog(@"Hello,World!");

[pool drain];
return 0;

}
即:xcode为开发者写的代码外层包了一层NSAutoreleasePool。这个池(pool)类似数据结构中的堆栈(Stack),相当于一个容器,每次对象调用autorelease方法时(obj-c中的正式说法应该是:对象发送autorelease消息),对象的引用计数并不真正变化,而是向pool中添加一条记录,记下对象的这种要求。最后当pool发送drain或release消息时,池中的所有对象的这种要求一一被执行(即:pool被销毁前,会通知池中的所有对象,全部发送release消息真正将引用计数减少,如果对象之前有发送过autorelease消息)

下面看一下基本的使用,先给Sample添加一个属性int型的flag(用于在销毁时看到是哪一个实例正在被销毁),同时重写dealloc()以便在释放时能输出一些信息

Sample.h

#import

@interface Sample : NSObject {

}

-(NSString*) toString;

//增加一个int型的属性flag
@property int flag;

@end
Sample.m

#import "Sample.h"

@implementation Sample

//自动生成属性flag的setter与getter方法
@synthesize flag;

-(NSString*) toString
{
NSString* str = [[NSString alloc] initWithFormat:@"This is Sample%d",flag];
return str;
}

-(void)dealloc
{
NSLog(@"Sample %d is going to die.",flag);
[super dealloc];
}
@end
使用自动释放池后的main函数

#import
#import "Sample.h"

int main (int argc, const char * argv[]) {

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

Sample *s1 = [Sample new];
Sample *s2 = [Sample new];

s1.flag=1;
s2.flag=2;

[s1 autorelease];
[s2 autorelease];

[pool drain];
return 0;

}
运行结果为:

2011-02-24 13:28:09.759 MemoryManage[282:a0f] Sample 2 is going to die.
2011-02-24 13:28:09.769 MemoryManage[282:a0f] Sample 1 is going to die.

从结果上看,pool是后进先出的,即:最后autorelease的最先释放(符合stack数据结构的特征)。再回到前面提到的toString方法中内存泄漏的问题,明白pool的基本原理后,只要把return str换成retrun [str autorelease]就行了,即把该字符串在池中登记,这样当[pool drain]时,所有登记的对象,将自动调用release方法,从而得到释放。

自动释放池从功能上可以理解为一种延时释放技术:即通过发送autorelease消息,向自动释放池登记,表明自己将来会在pool销毁时,一并发送release消息销毁自己。

最后提几点注意事项:

1、NSAutoreleasePool的实例pool本身也是一个对象,同样需要释放,所以最后也同样需要[pool release]或[pool drain],也正是这一行代码,才会将池中的所有对象同时释放。--注:drain仅适用于max os高版本,低版本不适用,而release通用,其它并无太大差别

2、pool在release时,仅仅只是简单的让所有池中的对象都发送release而已,并无其它玄机。(即:让池中所有对象的引用计数减1) 所以,如果你在之前用代码强制retain了某对象的引用计数,即使pool被release了,池中的对象仍然有可能因为引用计数仍大于1,而未被销毁。比如下面的代码:

#import
#import "Sample.h"

int main (int argc, const char * argv[]) {

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

Sample *s1 = [Sample new];//retainCount=1
Sample *s2 = [Sample new];//retainCount=1

s1.flag=1;
s2.flag=2;

NSLog(@"s1.retainCount=%d,s2.retainCount=%d",[s1 retainCount],[s2 retainCount]);

[s1 autorelease];//retainCount仍为1

NSLog(@"s1.retainCount=%d,s2.retainCount=%d",[s1 retainCount],[s2 retainCount]);

[s2 retain];//retainCount=2
[s2 autorelease];//retainCount仍为2

NSLog(@"s1.retainCount=%d,s2.retainCount=%d",[s1 retainCount],[s2 retainCount]);

[pool drain];

return 0;

}
运行结果:

2011-02-24 13:49:22.558 MemoryManage[461:a0f] s1.retainCount=1,s2.retainCount=1
2011-02-24 13:49:22.566 MemoryManage[461:a0f] s1.retainCount=1,s2.retainCount=1
2011-02-24 13:49:22.567 MemoryManage[461:a0f] s1.retainCount=1,s2.retainCount=2
2011-02-24 13:49:22.578 MemoryManage[461:a0f] Sample 1 is going to die.

因为s2发送了retain消息,从而让其引用计数上升为2,所以最终即使pool释放后,s2仍未销毁。

3、在iphone/ipad等内存有限的手持设备上,并不建议使用autorelease,因为说到底这是一种延时释放,如果你的程序一直在跑,代码尚未执行到[pool release]之前,即使有很多对象不再需要了,但它们占用的内存并未真正释放。

4、不要把大量循环操作放到同一个NSAutoreleasePool之间,道理同上,这样会使池中有大量对象,导致程序在运行时占用较多内存。比如下面这段代码:

int main (int argc, const char * argv[]) {

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

int i;

for (i=0;iobjective-C 的内存管理之-自动释放池(autorelease pool)

时间: 2024-12-30 21:28:07

objective-C 的内存管理之-自动释放池(autorelease pool)的相关文章

手动内存管理和自动释放池

手动内存管理 在进行内存管理的时候要注意内存管理的准则:谁开辟内存,谁释放内存(谁污染的谁治理) .开辟内存之后,对象的引用计数为1,只有继承自NSObject的对象才有内促管理的概念, 当对象引用计数为0的时候对象的内存会被清理. 下列关键字会开辟内存,对象引用计数器+1 alloc new copy mutableCopy 下列是内存管理的相关方法. retain :保留对象,对象的引用计数器+1. release : 释放对象,对象引用计数器-1. retainCount : 获取对象当前

Objective-C(8)内存管理之自动释放池

自动释放池 是一种半自动的内存管理方式 autorealease方法: - (instancetype)autorelease 此方法将对象放到自动释放池中,当自动释放池销毁时,池中的所有对象都会随之销毁. 常见的使用方式: Person *p = [[[Perosn alloc] init] autorelease]; 使用@autoreleasepool关键字来使用自动释放池 其后的{-}相当于自动释放池的生存期 ,如: @autoreleasepool {      Person *p =

Objective-C----MRC内存管理 、 自动释放池 、 面向对象三大特性及封装 、 继承 、 组合与聚合

1 MRC练习 1.1 问题 引用计数是Objective-C语言采用的一种内存管理技术,当一个对象被创建在堆上后,该对象的引用计数就自动设置为1,如果在其它对象中的对象成员需要持有这个对象时,则该对象的引用计数被加上1,此时如果该对象被释放,内存管理程序将首先把该对象的引用计数减1,然后判断该对象的引用计数是否为0,由于其它对象在持有该对象时将引用计数加了1,所以此时该对象的引用计数减1后不为0,则内存管理程序将不会释放该对象.直到持有该对象的其它对象也被释放时,该对象的引用计数再次减1,变为

Autorelease自动释放池的使用

Autorelease自动释放池的使用 使用ARC开发,只是在编译时,编译器会根据代码结构自动添加了retain.release和autorelease. MRC内存管理原则:谁申请,谁释放 遇到alloc/copy/retain 都需要添加release或autorelease autorelease 只是一个标记,表明会延迟释放 当一个autorelease对象超出自己的作用域后,会被添加到离他最近的autorelease pool中,当pool开始倾倒的时候,会向池里面所有的对象发送一次r

cocos2D-x 3.5 引擎解析之--引用计数(Ref),自动释放池(PoolManager),自动释放池管理器( AutoreleasePool)

#include <CCRef.h> Ref is used for reference count manangement. If a classinherits from Ref. Class Ref 为引用计数类,用来管理对象的引用计数.这样就不会出现还有指针保持指向该对象,当使用该指针操作时,如果指向的对象被销毁就会出现程序异常. class CC_DLL Ref { public: void retain();//增加引用计数一次 void release();//减少引用计数一次,

29-oc自动释放池

autorelease基本概念 什么是自动释放池? autorelease是一种支持引用计数的内存管理方式,只要给对象发送一条autorelease消息,会将对象放到一个自动释放池中,当自动释放池被销毁时,会对池子里面的所有对象做一次release操作 自动释放池的优点是什么 不用再关心对象释放的时间 不用再关心什么时候调用release 简述自动释放池的原理 autorelease实际上只是把对release的调用延迟了,对于每一个autorelease,系统只是把该 Object放入了当前的

OC基础(十三)autorelease自动释放池

autorelease 自动释放池 autorelease是一种支持引用计数的内存管理方式,只要给对象发送一条autorelease消息,会将对象放到一个自动释放池中,当自动释放池被销毁时,会对池子里面的所有对象做一次release操作 优点:不用再关心对象释放的时间,不用再关心什么时候调用release 原理:autorelease实际上只是把对release的调用延迟了,对于每一个autorelease,系统只是把该 Object放入了当前的autorelease pool中,当该pool被

【iOS开发-34】自动释放池@autoreleasepool的使用注意事项以及ARC机制——面试必考内容

自动释放池@autorelease面试频率可能会吧release还要高. (1)在自动释放池@autoreleasepool{}中alloc一个对象后(如p1),仍然需要用[p1 autorelease];只是这个语句和[p1 release];不同,后者表示把p1的retainCount-1,而前者仅仅表示把p1放到自动释放池中返回一个self,自动释放池结束销毁时,统一对里面的对象引用计数retainCount-1. (2)@autoreleasepool{}可以随意创建,也可以嵌套使用.

黑马程序员--Objective-C之--自动释放池的概念以及使用

今天我们来看一下OC中数组对象在是如何处理对象元素的引用计数问题的,同时介绍一下自动释放池的相关概念 一.数组对象是如何处理对象元素的引用计数问题 // // main.m // 26_NSArrayMemeryManager // // Created by waterfoxjie on 15-03-29. // Copyright (c) 2015年 waterfoxjie. All rights reserved. // #import <Foundation/Foundation.h>