`
waynelewis
  • 浏览: 12969 次
  • 性别: Icon_minigender_1
  • 来自: 沈阳
社区版块
存档分类
最新评论

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

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

Sample.h类接口部分

#import < Foundation / Foundation.h >
@interface Sample : NSObject {

}

-(NSString*) toString;

@endSample.m类实现部分

#import "Sample.h"

@implementation Sample


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

#import <Foundation/Foundation.h>
#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 <Foundation/Foundation.h>

@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 <Foundation/Foundation.h>
#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 <Foundation/Foundation.h>
#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;i<10000;i++){
Sample *s = [Sample new];
s.flag = i;
NSLog(@"%@",[s toString]);
[s autorelease];
}
  
[pool release];
return 0;
   
}
上面的代码运行时,必须等整个循环结束后才开始销毁对象。可以改进为下面这样:

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

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

int i;

for (i=0;i<10000;i++){
Sample *s = [Sample new];
s.flag = i;
NSLog(@"%@",[s toString]);
[s autorelease];
if (i % 100==0)
{
[pool release];
pool = [[NSAutoreleasePool alloc] init];
}
}
  
[pool release];
return 0;
   
}
这样每当池子里有100个对象时,就释放一次,这样程序在运行时占用的内存就会少很多

最后从书上抄一段号称Cocoa内存管理的黄金定律:如果我使用了new、alloc或copy方法获得一个对象,则我必须释放(release)或自动释放(autorelease)该对象


作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
分享到:
评论

相关推荐

    《深入浅出发Objective-C》 内存管理.pdf

    此外,Objective-C还提供了自动释放池(Autorelease Pool),允许对象在自动释放池中注册后,由自动释放池来管理内存的释放时机。 为了简化代码,Objective-C引入了宏定义来帮助开发者自动执行释放内存的操作。例如...

    Objective-C中的Autorelease Pool揭秘:深入理解自动释放机制

    此外,Objective-C 的内存管理机制也是其核心组成部分之一,尤其是 Autorelease Pool 这一概念,在现代 Objective-C 编程中扮演着至关重要的角色。 #### 1. Autorelease Pool 基础 Autorelease Pool 是 Objective-...

    objective-c内存管理

    Objective-C还提供了一个自动释放池(autorelease pool),可以在一定程度上简化内存管理。当对象被添加到自动释放池后,它会在适当的时候自动释放。 - **对象自动释放**:新生成的对象只需要调用`autorelease`方法...

    Objective-C内存管理课件.doc

    内存管理是编程中至关重要的部分,Objective-C 在内存管理方面采用了引用计数(Reference Counting)策略,直到iOS 5.0后引入了ARC(Automatic Reference Counting),但这并不改变对旧版内存管理机制的理解。...

    Objective-C内存管理教程和原理剖析

    为了简化内存管理,Objective-C引入了`autorelease pool`。新创建的对象可以通过`autorelease`自动释放,无需手动调用`release`。例如,`ClassA *obj1 = [[[ClassA alloc] init] autorelease];`在这里,`autorelease...

    Objective-C 入门指南 中文

    - **自动释放池(Autorelease Pool)**:帮助管理临时对象的生命周期。 #### 六、标准框架 - **Foundation 框架**:提供了基础的数据类型和集合类。 - `NSArray`:固定长度数组。 - `NSDictionary`:键值对集合...

    Objective-C基础知识

    - 用于延迟对象的释放,直到当前的 autorelease pool 被释放。 - 示例: ```objective-c NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // ... [pool drain]; ``` #### Foundation框架类 -...

    objective-c简体中文手册

    - **Autorelease Pool**:自动释放池机制用于批量释放对象,减少内存管理负担。 #### 六、Foundation框架 - **NSArray**:不可变数组,用于存储和管理对象集合。 - **NSDictionary**:不可变键值对集合,用于存储...

    Objective-C入门指南.pdf

    - `autorelease pool`用于延迟释放对象。 #### 六、Foundation框架 - **数组(NSArray)**: - 一种存储和操作对象集合的数据结构。 - **字典(NSDictionary)**: - 键值对形式的集合,用于快速查找和存储对象。 ###...

    Objective-C入门指南

    Objective-C是苹果公司开发iOS和macOS应用的主要语言之一,在移动开发领域具有重要的地位。 #### 二、环境搭建 - **Linux/FreeBSD**: 安装GNUStep,可以通过执行`GNUstep.sh`脚本来配置环境。根据不同的Shell类型...

    Objective-C 培训教程

    `:创建了一个自动释放池,用于管理内存释放。当程序结束时,通过调用`[pool drain]`方法,可以一次性释放所有在池中的对象。 - `NSLog(@"Programming is fun!");`:用于打印日志信息,类似于C语言中的`printf`函数...

    objective—c内存管理教程和原理

    4. **自动释放池(Autorelease Pool)**:为了简化内存管理,Objective-C引入了自动释放池的概念。通过自动释放池,可以在遵守一定规则的情况下自动释放对象,而无需手动调用`release`。当对象创建时,可以将其放入...

    objective-c 中文手册

    - **Autorelease Pool**:自动释放池机制,帮助管理对象的内存释放。 #### 七、Foundation Framework Classes - **NSArray**:不可变数组类,用于存储对象集合。 - **NSDictionary**:键值对映射容器,用于存储对象...

    Objective-C的语法与Cocoa框架

    6.Autorelease Pool:Objective-C中的内存管理使用引用计数,autorelease池用于自动释放不再使用的对象,防止内存泄漏。 7.GCD(Grand Central Dispatch):Apple的多线程解决方案,提供并发任务调度、队列管理等...

    Objective-C Beginner's Guide

    - **自动释放池(Autorelease Pool)**: 提供一种机制来自动释放不再使用的对象。 #### 三、Foundation框架类 - **NSArray**: 有序集合的类。 - **NSDictionary**: 键值对集合的类。 #### 四、优点与缺点 **4.1 ...

    cocos2dx3.2关于自动释放池的demon源代码

    自动释放池(AutoreleasePool)是Objective-C中用于管理对象生命周期的一种机制,但在Cocos2dx中也得到了应用。它的工作原理类似于Java或C#的垃圾回收机制,但更为轻量级。当对象被添加到自动释放池时,会在稍后某个...

    OC-自动释放池的使用

    自动释放池(Autorelease Pool)是OC内存管理机制的一部分,它帮助开发者有效地管理内存,尤其是在循环中创建大量临时对象时。理解并正确使用自动释放池对于避免内存泄漏和优化性能至关重要。 自动释放池的基本概念...

Global site tag (gtag.js) - Google Analytics