iOS内存管理下

来源:互联网 发布:淘宝衣服是死人衣服吗 编辑:程序博客网 时间:2024/06/04 19:25

iOS内存管理

按照上一节所讲内存,对如下例子进行代码实现
/***************************************
1、创建Person类
2、创建Car类
3、分别创建Person对象和Car对象
4、人可以拥有一辆车 Car1
5、人更换了一辆车 Car2
******************************************/
  1. #import <Foundation/Foundation.h>
  2. @interface Car : NSObject
  3. {
  4. int  _speed; //车的速度,用来区别每一辆车
  5. }
  6. - (void)setSpeed:(int)speed;
  7. - (int)speed;
  8. @end
  9.  //////////////////////////////////////
  10. @implementation Car
  11. - (void)setSpeed:(int)speed
  12. {
  13. _speed = speed;        //基本数据类型,直接赋值
  14. }
  15. - (int)speed
  16. {
  17. return _speed;
  18. }
  19. - (void)dealloc
  20. {
  21. NSLog(@“速度为%d的Car对象被释放”,_speed);
  22. [super dealloc];
  23. }
  24. @end


  25. @interface Person : NSObject
  26. {
  27. Car *_car;    //人拥有一辆车
  28. }
  29. - (void)setCar:(Car *)car;
  30. - (Car *)car;
  31. @end
  32. /////////////////////////////////////
  33. @implementation Person
  34. - (void)setCar:(Car *)car
  35. {
  36. _car = [car retain];     //OC数据类型,赋值之前,需要retain,是计数器加1
  37. }
  38. - (Car *)car
  39. {
  40. return _car;
  41. }
  42. - (void)dealloc
  43. {
  44. [_car release];
  45. NSLog(@“Person对象被释放");
  46. [super dealloc];
  47. }
  48. @end


  49. int main()
  50. {
  51. Person *p = [[Person alloc]init];
  52. Car *c1 = [[Person alloc]init];
  53. c1.speed = 100;
  54. p.car = c1;
  55.   [c1 release]
  56. [p release];
  57. return 0;
  58. }


上述代码中,通过执行main函数后,创建的所有对象均被释放,结果如下:
968167.png

如果将main函数的59行处增加如下代码:
692464.png
再执行main函数,其结果如下:
410760.png
通过执行的结果发现,并没有将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函数,结果如下:
582844.png


其实,按照上述对set方法更改后,还并不是最完善的,我们继续对main函数进行如下更改:
138041.png
再次运行后,会出现僵尸对象错误报错,结果如下:
60300.png

这又是为什么呢?对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];
}











0 0
原创粉丝点击