iOS内存管理
按照上一节所讲内存,对如下例子进行代码实现
/***************************************
1、创建Person类
2、创建Car类
3、分别创建Person对象和Car对象
4、人可以拥有一辆车 Car1
5、人更换了一辆车 Car2
******************************************/
- #import <Foundation/Foundation.h>
- @interface Car : NSObject
- {
- int _speed; //车的速度,用来区别每一辆车
- }
- - (void)setSpeed:(int)speed;
- - (int)speed;
- @end
- //
- @implementation Car
- - (void)setSpeed:(int)speed
- {
- _speed = speed; //基本数据类型,直接赋值
- }
- - (int)speed
- {
- return _speed;
- }
- - (void)dealloc
- {
- NSLog(@“速度为%d的Car对象被释放”,_speed);
- [super dealloc];
- }
- @end
- @interface Person : NSObject
- {
- Car *_car; //人拥有一辆车
- }
- - (void)setCar:(Car *)car;
- - (Car *)car;
- @end
- /
- @implementation Person
- - (void)setCar:(Car *)car
- {
- _car = [car retain]; //OC数据类型,赋值之前,需要retain,是计数器加1
- }
- - (Car *)car
- {
- return _car;
- }
- - (void)dealloc
- {
- [_car release];
- NSLog(@“Person对象被释放");
- [super dealloc];
- }
- @end
- int main()
- {
- Person *p = [[Person alloc]init];
- Car *c1 = [[Person alloc]init];
- c1.speed = 100;
- p.car = c1;
- [c1 release]
- [p release];
- return 0;
- }
上述代码中,通过执行main函数后,创建的所有对象均被释放,结果如下:
如果将main函数的59行处增加如下代码:
再执行main函数,其结果如下:
通过执行的结果发现,并没有将c1对象释放,会出现内存泄露
我们始终遵循着,谁retain,谁release的原则,为什么会出现上述内存泄露的情况呢???
对main函数原因分析如下:
①当执行Person *p = [[Person alloc]init]时
p对象的内存计数器为1
②当执行Car *c1 = [[Person alloc]init]时
c1所指向对象的内存计数器为1
③当执行c1.speed = 100时
由于_speed是基本数据类型,无须管理
④当执行p.car = c1时,会调用Person对象的set方法,进入到36行处开始执行
>>当执行到38行_car = [car retain]时
c1所指向的内存计数器加1,变为2
⑤当执行Car *c2 = [[Person alloc]init]时
c2所指向对象的内存计数器为1
⑥当执行p.car = c2时,会再次调用Person对象的set方法,进入到36行处开始执行
>>当执行38行_car = [car retain]时
c2所指向的内存计数器加1,变为2
⑦当执行c2 release时
c2所指向的内存计数器减1,变为1
⑧当执行c1 release时
c2所指向的内存计数器减1,变为1
⑨当执行p release时,p所指向内存的计数器减1,变为0,此时进入到dealloc方法
>>当执行到dealloc方法中的46行时,_car此时所指向的对象为c2
也就是说,c2所指向的内存计数器减1,变为0,被销毁
>>当执行到dealloc方法中的47行时,打印出日志
所以,在整个代码执行过程中,只有Person对象和C2对象被释放,c1对象并没有被释放,故出现内存泄露
为什么会出现这样的情况呢,实际上我们并没有完全遵守内存管理的原则,在上述例子中,当创建了c1对象,在人对象拥有它的时候,做了retain操作,但是当人对象更换车对象的时候,其实已经不再占有c1对象。也就是说在set方法中,需要对旧车进行release处理。故,需要在Person的setCar方法中增加如下代码:
- (void) setCar : (Car *)car
{
[_car release]; //对旧车进行release操作
_car = [car retain];
}
更改后,再次运行main函数,结果如下:
其实,按照上述对set方法更改后,还并不是最完善的,我们继续对main函数进行如下更改:
再次运行后,会出现僵尸对象错误报错,结果如下:
这又是为什么呢?对main函数原因分析如下:
①在执行第一个p.car = c1之前,各对象内存计数器如下
p指向内存计数器为1
c1指向内存计数器为1
②当执行第一个p.car = c1时,调用setCar方法,各对象内存计数器如下
>>当执行setCar方法中的_car release方法时(对旧车进行释放的代码)
此时_car为空指针,不会报错,不进行任何操作
>>当执行setCar方法中的_car = [car retain]方法时,
此时_car指向c1所指向的对象,且c1所指向内存对象计数器加1,变为2
③当执行main函数中的c1 release时
c1所指向的内存计数器减1,变为1
④当执行main函数中的第二个p.car = c1时,调用setCar方法,各对象内存计数器如下
>>当执行setCar方法中的_car release方法时(对旧车进行释放的代码)
此时_car指向的是c1所指向的对象,且c1所指内存对象计数器减1,变为0,c1所指向的内存对象被释放
>>当执行setCar方法中的_car = [car retain]时,其实执行的是_car = [c1 retain]
而此时c1所指向的内存对象已经被释放,c1指向的对象也僵尸对象,c1为野指针。故会报错
为了解决上述问题,避免重复赋值而引起的野指针报错,我们需要在set方法中继续完善代码,如下
- (void)setCar:(Car *)car
{
if(car != _car) //在赋值之前,进行判断,如果重复赋值,不执行代码
{
[_car release]; //对就对象进行release操作
_car = [car retain]; //新对象retain操作,且对_car赋值
}
}
通过上述的更改,我们对set方法以及dealloc方法进行了代码更改,形成了比较严谨的内存管理代码
总结:(内存管理代码规范)
1、只要使用了alloc,必须要有release(autoRelease)
如果没有使用alloc,不需要增加release
2、set代码规范
①基本数据类型———>直接进行赋值,
- (void)setSpeed:(int)speed
{
_speed = speed;
}
②OC对象类型,需要判断且处理旧对象问题
-(void)setCar:(Car *)car
{
if(car != _car)
{
[_car release];
_car = [car retain];
}
}
3、dealloc方法代码规范
①一定要在末尾调用[super dealloc]
②对当前对象所拥有的其他对象进行release
-(void)dealloc
{
[_car relase];
[super dealloc];
}