`
waynelewis
  • 浏览: 12400 次
  • 性别: 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
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
分享到:
评论

相关推荐

    Google Objective-C Style Guide 中文版

    Google Objective-C Style Guide 中文版 目录 例子 空格与格式 空格与制表符 行宽 方法声明与定义 方法调用 @public与@private 异常 协议 命名 文件名 Objective-C++ 类名 分类名 Objective-C方法名 变量名 注释 ...

    Objective-c对象组装XML

    -tag 1- 测试无内存泄露 1 创建数据对象() NSMutableDictionary *map = [[NSMutableDictionary alloc]init]; [map setObject:@"a" forKey:@"author"]; [map setObject:@"b" forKey:@"title"]; [map set...

    mulle-clang::ID_button:用于mulle-objc的Objective-C编译器

    mulle-clang 这是一个基于clang 10.0.0的Objective-C编译器,是为运行时编写的。 它对应于mulle-objc-runtime v0.17或更高版本。 有关的更多信息,请... 您不能执行显式的内存管理(如-dealloc , -autorelease ,

    Programming in Objective-C 4th Edition

    Object References and the Autorelease Pool 403 The Event Loop and Memory Allocation 405 Summary of Manual Memory Management Rules 407 Automatic Reference Counting (ARC) 408 Strong Variables 408 Weak ...

    Objective-C学习之ARC的实现方法

    “自动引用计数(ARC)是一个编译器级的功能,它能简化Cocoa应用中对象生命周期管理(内存管理)的流程。” ARC使内存管理在大部分时候变得如同小事一桩,但我们仍要在决定自己的类如何管理其它对象的引用

    Objective C Memory Management Essentials(PACKT,2015)

    Objective-C Memory Management Essentials will familiarize you with the basic principles of Objective-C memory management, to create robust and effective iOS applications. You will begin with a basic ...

    前端开源库-node-circleci-autorelease

    前端开源库-node-circleci-autorelease节点circleci autorelease,在circleci上创建释放标记

    objective-c-tutorial:这是我在学习时编写的所有目标c教程代码

    客观教学这是我在学习时编写的所有目标c教程代码#基本语法正常的printf操作NSlog示例autoRelease池示例#NSString操作NSString操作在这里NSRange示例NSMutableString示例#NSArray示例NSArray示例NSArray函数示例...

    Node.js-mysql-autoRelease:node.js mysql事务自动释放连接

    Node.js-mysql-autoRelease node.js mysql transaction Auto Release connection\ 实现Node.js中连接池自动回收连接功能

    02-内存管理1

    1&gt; 系统自带的绝大数类方法返回的对象,都是经过autorelease的4.block在ARC中和MRC中的用法有什么区别,需要注意什么1.对于没有引用外部变量

    object-c基础教程

    大纲  开始吧 o 下载这篇教学 o 设定环境 o 前言 o 编译 hello world  创建 Classes ...o Autorelease Pool  Foundation Framework Classes o NSArray o NSDictionary  优点与缺点  更多信息

    IOS高级内存管理编程指南.pdf

    介绍IOS内存管理的一本电子书,初学者可以很快的了解ios上的内存管理的机制,有一定经验的也可以从中得到新的收获。

    autorelease-github:通过 GitHub API 为您的构建管道自动发布

    autorelease-github 通过 GitHub API 为您的构建管道自动发布 这是一个非常简单的项目,它需要 N 个文件并使用 bash、curl 和 jq 通过上传它们。 它旨在成为构建管道中的嵌入式发布构建。 输入是一堆文件和一个放...

    在非ARC环境下的内存管理

    这是一个简单的内存管理的示例,在非ARC的环境下,如何管理内存

    iPhone开发、ObjectiveC_面试题目

    8、autorelease的对象是在什么时候被release的? 9、这段代码有什么问题,如何修改 10、for (int i = 0; i ; i++) { NSString *string = @”Abc”; string = [string lowercaseString]; string = [string ...

    详解iOS应用开发中的ARC内存管理方式

    ARC是自iOS 5之后增加的新特性,完全消除了手动管理内存的烦琐,编译器会自动在适当的地方插入适当的retain、release、autorelease语句。你不再需要担心内存管理,因为编译器为你处理了一切 注意:ARC 是编译器特性,...

    AutoReleaseTool:自动部署桌面应用程序的CICD工具

    通过在完整的CI / CD管道配置中使用AutoRelease,为您的桌面应用程序创建新版本并将其部署到其用户所需的全部过程很简单: 将您的更改推送到定义的github版本分支触发一个webhook,该webhook将在 WM中启动构建过程...

    iOS 5 ARC完全指南

    Xcode 的ARC 自动迁移 9 Xcode 的自动迁移工具 9 转换后的其它问题 14 禁止某些文件的ARC 14 ARC 自动迁移的常见问题 15 属性property 17 IBOutlet 18 readonly property 19 autorelease 、release 、retain...

    有时侯我们需要延迟一个对象的引用计算减一操作

    我们需要先创建一个auto release pool,才能有效地实现autorelease机制,否则会导致内存泄露。当一个对象obj发送autorelease消息时,会发生如下过程:

    Auto Release Sh*t-crx插件

    语言:中文 (简体) 自动生成发版申请 它根据地址自动生成发版申请单,故名:auto-release-sh*t

Global site tag (gtag.js) - Google Analytics