objective-C 的内存管理之-实例分析

news/2024/7/20 16:11:26 标签: 内存管理, xcode, 移动开发

注:这是《Objective-C基础教程》一书上的实例,但是原书限于篇幅,分析得比较简单,初次阅读看得比较费劲,这里展开详细讨论一下。

场景:有二个类Car和Engine,即“汽车”和“引擎”。

先来看最初的版本:

Engine.h

#import <Cocoa/Cocoa.h>

@interface Engine : NSObject

@property int flag;

@end // Engine

Engine.m

#import "Engine.h"

@implementation Engine

@synthesize flag;

- (NSString *) description
{
    return ([NSString stringWithFormat:@"I am engine %d,my retainCount=%d",flag,[self retainCount]]);
} // description


-(void) dealloc
{
	NSLog(@"this engine %d is going to die.",flag);
	[super dealloc];
	NSLog(@"this engine %d is dead.",flag);
}
@end // Engine

代码不复杂,略加解释:Engine类有一个flag属性,用于后面辅助输出时区分当前引擎的唯一标识。然后就是description方法(相当于c#中Object的toString()方法),用于返回一个描述自身的字符串。最后就是dealloc方法,用于清理自身所用的资源。

Car.h

#import <Cocoa/Cocoa.h>

#import	"Engine.h"

@interface Car : NSObject
{
    Engine *engine;	
}

@property int flag;

- (void) setEngine: (Engine *) newEngine;

- (Engine *) engine;

@end // Car

Car.m

#import "Car.h"
#import "Engine.h"


@implementation Car

@synthesize flag;

- (id) init
{
    if (self = [super init]) {
        engine = [Engine new]; //每辆汽车诞生时,先预设了一个空的引擎(flag=0的engine),这个对象最终也需要释放!       
    }
    return (self);
} // init


- (Engine *) engine
{
    return (engine);
} // engine


- (void) setEngine: (Engine *) newEngine
{
	engine = newEngine;    
} // setEngine


-(void) dealloc
{	
	NSLog(@"the car %d is going to die.",flag);
	NSLog(@"%@",engine);
	[engine release];//释放附属资源:引擎	
	[super dealloc];
	NSLog(@"the car %d is dead.",flag);
}

@end // Car

解释一下:init方法中,给每辆汽车在出厂时预置了一个默认的引擎(其flag值为默认值0),然后setEngine方法用于给汽车设置新引擎,最后dealloc中,汽车销毁时会附带release自己的引擎。

先来考虑第一种情况

有一辆汽车,给它安装了新引擎,使用完后汽车销毁,但是引擎还能拿出来做其它用途(比如给其它汽车使用之类),最后新引擎也用完了,销毁!

Car *car1 = [Car new];
car1.flag  = 1;
Engine *engine1 = [Engine new];
engine1.flag = 1;
[car1 setEngine:engine1];
[car1 release];	
NSLog(@"%@",engine1);//这里模拟引擎做其它用途	
[engine1 release];

以上代码至少有二个问题:

1.1 Car在构造函数init里,预置的默认引擎(即flag=0的引擎)最后未被释放

1.2 Car在dealloc方法中,已经释放了engine,所以Car释放后,该引擎也就跟着灰飞烟灭了,没办法再做其它用途。所以第7,8行代码根本没办法运行,会直接报错!这比内存泄漏更严重。

先来解决最严重的第2个问题,至少让它跑起来再说,根源在于:Car销毁时,附带把engine也给release了!解决它的途径有二种:

1、去掉Car.m类dealloc中的[engine release],但是本着“自家的孩子自己管”的原则,不推荐这种不负责任的做法。

2、在setEngine方法中,人工调用[newEngine retain]方法,让引擎的引用计数加1,这样正好可抵消Car.m类dealloc方法中[engine release]带来的影响(一加一减,正好抵消!)。

于是Car.m中的setEngine方法有了第二个版本:

- (void) setEngine: (Engine *) newEngine
{
	engine = [newEngine retain];    
} // setEngine

再次编译,总算通过了,也能运行了。先把问题1.1丢到一边,再来考虑第二种情况

又有一辆汽车,安装了新引擎engine1,然后试了一下,觉得不爽,于是把engine1丢了,然后又换了另一个引擎engine2(喜新厌旧!)

Car *car1 = [Car new];
car1.flag = 1;
Engine *engine1 = [Engine new];
engine1.flag = 1;
[car1 setEngine:engine1];//换新引擎engine1		
[engine1 release];//觉得不爽,于是把engine1扔了

Engine *engine2 = [Engine new];
engine2.flag = 2;
[car1 setEngine:engine2];//又换了新引擎engine2

[car1 release];//使用完以后,car1报废
[engine2 release];//新引擎engine2当然也不再需要了

同样有二个问题:

2.1 engine1先被new了一次,然后在setEngine中又被retain了一次,也就是说其retainCount为2,虽然代码中后来release了一次,但是也只能让retainCount减到1,并不能销毁!

2.2 刚才1.1中所说的问题依然存在,即Car在init方法中预置的默认引擎engine0,始终被无视了,未得到解脱。

可能,你我都想到了,在setEngine方法中,可以先把原来的旧引擎给干掉,然后再把新引擎挂上去,这样就ok了! 好吧,setEngine的第三个版本出现了:

- (void) setEngine: (Engine *) newEngine
{
	[engine release];
	engine = [newEngine retain];    
} // setEngine

貌似皆大欢喜了,但是事情还没完,又有新情况了:第三种情况

有二辆汽车Car1与Car2,Car1换了新引擎engine1,然后跑去跟Car2显摆,Car2觉得新引擎不错,于是要求跟Car1共用新引擎engine1,但问题是:在Car2尚未下手前,engine1已经被某人(可能是car1自己,也可能是车主main()函数)给抛弃了!

Engine *engine1 = [Engine new];//engine1.retainCount=1
engine1.flag = 1;
	
Car *car1 = [Car new];
car1.flag = 1;
	
Car *car2 = [Car new];
car2.flag = 2;
	
[car1 setEngine:engine1];//car1换了新引擎engine1	
[engine1 release];//然后很快又抛弃了它
	
[car2 setEngine:[car1 engine]];//car2要跟car1共用engine1
	
//最后car1跟car2都被车主main函数给扔了
[car2 release];
[car1 release];

问题:在16行[car2 release]时,car2已经彻底把engine1给销毁了(也许car2忘记了,engine1是它跟car1共同的财产),于是紧接着[car1 release]时,car1的dealloc方法在[engine release]时,意外发现engine1已经不在人世了,最终它愤怒了,整个程序也就罢工了!

setEngine的最后一个版本

- (void) setEngine: (Engine *) newEngine
{	
	[newEngine retain];
	[engine release];	
	engine = newEngine;
    
} // setEngine

其实就是把上一个版本的二行代码,拆分成了三行,变成了先retain,再release,看上去好象含义一样,但是仔细分析你会发现,如果当engine与newEngine为同一个对象的引用时(即这二指针指向的为同一块内存),且newEngine(其实也就是engine)的retainCount为1时,原来的版本会导致newEngine(其实也就是engine)销毁,而现在这样处理后,即会被保留下来。

最后验证一个最终版本是否能完美应付上面提到的三种情况:

第一种情况的运行结果:

2011-02-25 09:17:52.951 CarParts[257:a0f] this engine 0 is going to die.
2011-02-25 09:17:52.957 CarParts[257:a0f] this engine 0 is dead.
2011-02-25 09:17:52.959 CarParts[257:a0f] the car 1 is going to die.
2011-02-25 09:17:52.961 CarParts[257:a0f] I am engine 1,my retainCount=2
2011-02-25 09:17:52.962 CarParts[257:a0f] the car 1 is dead.
2011-02-25 09:17:52.966 CarParts[257:a0f] I am engine 1,my retainCount=1
2011-02-25 09:17:52.968 CarParts[257:a0f] this engine 1 is going to die.
2011-02-25 09:17:52.969 CarParts[257:a0f] this engine 1 is dead.

第二种情况的运行结果:

2011-02-25 09:19:30.639 CarParts[291:a0f] this engine 0 is going to die.
2011-02-25 09:19:30.644 CarParts[291:a0f] this engine 0 is dead.
2011-02-25 09:19:30.646 CarParts[291:a0f] this engine 1 is going to die.
2011-02-25 09:19:30.648 CarParts[291:a0f] this engine 1 is dead.
2011-02-25 09:19:30.650 CarParts[291:a0f] the car 1 is going to die.
2011-02-25 09:19:30.652 CarParts[291:a0f] I am engine 2,my retainCount=2
2011-02-25 09:19:30.653 CarParts[291:a0f] the car 1 is dead.
2011-02-25 09:19:30.655 CarParts[291:a0f] this engine 2 is going to die.
2011-02-25 09:19:30.657 CarParts[291:a0f] this engine 2 is dead.

第三种情况的运行结果:

2011-02-25 09:21:02.549 CarParts[324:a0f] this engine 0 is going to die.
2011-02-25 09:21:02.554 CarParts[324:a0f] this engine 0 is dead.
2011-02-25 09:21:02.556 CarParts[324:a0f] this engine 0 is going to die.
2011-02-25 09:21:02.558 CarParts[324:a0f] this engine 0 is dead.
2011-02-25 09:21:02.559 CarParts[324:a0f] the car 2 is going to die.
2011-02-25 09:21:02.561 CarParts[324:a0f] I am engine 1,my retainCount=2
2011-02-25 09:21:02.563 CarParts[324:a0f] the car 2 is dead.
2011-02-25 09:21:02.571 CarParts[324:a0f] the car 1 is going to die.
2011-02-25 09:21:02.573 CarParts[324:a0f] I am engine 1,my retainCount=1
2011-02-25 09:21:02.575 CarParts[324:a0f] this engine 1 is going to die.
2011-02-25 09:21:02.578 CarParts[324:a0f] this engine 1 is dead.
2011-02-25 09:21:02.587 CarParts[324:a0f] the car 1 is dead.

从输出结果上看,不管是哪一种情况,Car以及Engine资源最终都得到了释放!

 

2014-02-21 注:现在最新的xcode上,setEngine方法不管是第二个版本,还是第三个版本,对于第三种情况,都能正确释放所有资源。估计是xcode做了改进,这二个版本编译出来的代码,个人估计是相同的,原书上的分析应该是基于当时的xcode环境,友情提醒大家学习时注意。

转载于:https://www.cnblogs.com/yjmyzz/archive/2011/02/24/1964245.html


http://www.niftyadmin.cn/n/760025.html

相关文章

action form php 到另一个页面窗口的大小_Ajax异步请求PHP服务器,如何做到无阻塞响应...

新手一个ajax异步请求的问题&#xff0c;用$.post、$.get、$.ajax请求PHP服务器时&#xff0c;总是无法异步返回数据。经多次测试才发现&#xff1a;-- 不同浏览器&#xff0c;请求不同域名-不阻塞&#xff1a;无需实验-- 不同浏览器&#xff0c;请求同域名-不阻塞&#xff1a;…

程序局域网访问不了_通过ZeroTier访问无公网IP地址电脑共享文件

简单说一下ZeroTier其实也是一个内网的穿透工具之一&#xff0c;可以把它当虚拟局域网看待&#xff0c;把那些没有公网IP的个人计算机&#xff0c;服务器通通的加入到这个你单独创建的虚拟局域网当中&#xff0c;只要是有网的地方&#xff0c;安装好它们的客户端输入认证信息就…

c#、ASP.NET面试题(一)

2 .列举ASP.NET 页面之间传递值的几种方式。 答. 1).使用QueryString, 如....?id1; response. Redirect().... 2).使用Session变量 3).使用Server.Transfer 3. 一列数的规则如下: 1、1、2、3、5、8、13、21、34...... 求第30位数是多少&a…

读取nacos配置_Nacos(六):多环境下如何“管理”及“隔离”配置和服务

前言 前景回顾&#xff1a; Nacos&#xff08;五&#xff09;&#xff1a;多环境下如何“读取”Nacos中相应环境的配置 Nacos&#xff08;四&#xff09;&#xff1a;SpringCloud项目中接入Nacos作为配置中心 现如今&#xff0c;在微服务体系中&#xff0c;一个系统往往被拆分为…

运用 GNOME Specimen 检查字体

Toy Posted in AppsRSSTrackback 若是你想知道系统中所安插的字体真相什么样&#xff0c;那么运用 GNOME Specimen 吧。GNOME Specimen 可以让你检查系统中已安插的统统字体&#xff0c;并对各类字体住手比拟&#xff0c;以资助你住手选择。运用 GNOME Specimen 检查系统中已安…

修建Linux防火墙之小我用户设置防火墙-2

来源&#xff1a;网海拾贝 启动SSH管事器 在UNIX/Linux形状下&#xff0c;管事器轨范放置在/usr/local/sbin目录下&#xff0c;启动方式如下&#xff1a; # sshd # ps x 可以看到SSHD曾经启动了。如果不盼望每次重启动体系&#xff0c;都要手工运转启动SSHD&#xff0c;则可以自…

python简单项目举例_Python | Python学习之常用项目代码(一)

写在前面本篇是咸鱼日常撸视频的时候记录的一些代码实例&#xff0c;可以直接运用到项目中但是有些代码的可用性没有那么好&#xff0c;旨在分享思路&#xff0c;不喜勿喷~搭建ip代理池(简易版)推荐两个scrapy代理的项目撸视频的时候学到的代理池实例获取西刺代理的代理列表并存…

如何留住新员工

“企”无“人 ”则“止”&#xff0c;企业的一切事务都是人干出来的&#xff0c;有效的员工流失率有利于保持公司的活力。目前&#xff0c;企业界高呼“留人"&#xff0c;留的是往往是企业老员工&#xff0c;却忽视了对新进 员工关注。一位老板向我抱怨&#xff1a;我们的…