做ios手机游戏开发已有三个多月。从学习到还算熟练使用object-c开发ios程序,我感觉最难的问题还是在内存管理上。如何release数据和retain数据,这是一个让人小心翼翼才能去想清楚的问题。
这里我想记录一下我在程序中使用 NSMutableArray 时候遇到的EXC_BAD_ACCESS.
如果做ios程序有一段时间,那么一看到
EXC_BAD_ACCESS这个词,我们就该知道这是一个关于对象被release而引起的内存访问出错问题。
实际上,这个问题一度折磨了我好几个小时才找到了问题的源头。
在我的项目里声明了以下数组:
@interface FightLayer
{
NSMutableArray *blood;
}
#import"FightLayer.h"
@implement
-(void)fight_result:(NSDictionary *dic)
{
// 声明一个数组,分配一块内存空间
blood = [NSMutableArray alloc]init];
//我用这个数组存储从后台传过来的数据,如以下形式:
blood = [dic objectForKey:@"blood"];
}
@end
按说,blood已经声明成了全局变量,那么在FightLayer这个类里应该可以随便使用它,而不需要考虑内存泄漏的问题。这似乎是我们开发软件时一个非常想当然的经验。
但问题就在:当我在fight_result这个方法之外操作blood这个数组时,就出现了EXC_BAD_ACCESS。为什么呢?是因为我没有初始化NSMultablesArray的方法有问题吗?
那我改成 :
blood = [NSMutableArray alloc]init]retain]; ?
但我又发现:在fight_result下一个执行的方法可以成功访问到blood数据,但是在下下一个方法里访问blood数据时就又报
EXC_BAD_ACCESS。这就让人郁闷了。
blood明显是全局变量,除了在dealoc方法里我release掉blood之外,其他地方没有去release它啊,为什么还会造成内存访问失败的问题?之后我还尝试了把blood做成属性
@property(nonamic,retain)NSMutableArray *blood;
但仍然没有解决问题,那么,问题不是在retain上了,即使使用retain,还是在哪里被release掉了。我需要找到这个release的点。
这个时候我重温了ios的六个内存管理原则,在这六句话里我找到了问题的原因:
1. 通过分配或复制创建的对象保持计数1
2. 假设从任何别的方法获取的对象保持计数1,而且在自动释放池中. 要想在当前执行范围外使用该对象,就必须保持它
3. 向集合添加对象时它就被保持,从集合移除对象时就被释放.释放集合对象会释放该集合中的所有对象
4. 确保有多少alloc,copy,mutableCopy或retain消息就有多少release或autorelease消息发送给该对象. 换句话说,确保你的代码平衡
5. 在访问方法设置属性,先保持,再释放 (ztime: 现在有@propperty , @synthesize 两个指令自动创建此代码)
6. 用@"..."结构创建的NSString对象是常量.发送release或retain并无效果
这六点中的第二点是我出现问题的原因。我用NSMutableArray来接受后台传送的字典。也就是说,我申请的blood数组实际上是从别的方法获取的对象,那么它就是在自动释放池里了,在fight_result执行结束后,它就被释放了,所以在fight_result执行范围之外,我还需要使用
NSMutableArray对象blood,我就必须保持住它。
如下方法皆可以达到目的
:
blood = [dic objectForKey:@"blood"]retain];
or blood = [dic objectForKey:@"blood"]copy];
注意这个地方不能用:
blood = [NSMutableArray alloc]init]retain];来试图保留住对象。
这里还想详细记录下 retain 和 copy的使用。
retain:
释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1
copy:
建立一个索引计数为1的对象,然后释放旧对象
这个地方还可以使用mutableCopy:
blood = [dic objectForKey:@"blood"]mutableCopy];
和copy的区别是:用它复制得到数据,程序员将有对新数据进行更改的权利。而用copy复制得到的数据,将不能对数据进行更改操作,而只能读出。