ios应用开发Objective-C内存管理基础


  关于我们.net开发人员来说,.net为我们提供了自动内存治理的机制,我们不需去关怀内存的治理 。然而iPhone开发中却是不能的 。这篇文章将简述一下Objective-C的内存治理机制和 步骤和一些 特点 。

  手动的进行内存治理

  Cocoa和Objective-C的类都是NSObject的子类 。NSObject中有几个 步骤进行内存治理 。alloc 步骤为对象 调配一片内存空间 。dealloc 步骤用于 开释对象的空间 。然而在我们的代码中将永远都不会 使用dealloc 步骤,由于运行时会为你调用此 步骤 开释内存空间 。而你需求做的只不过 引用计数,稍后介绍什么是 引用计数 。

  除了alloc和dealloc,NSObject的还有retain和release 步骤两个 步骤用于 引用计数 。retain 步骤给retainCount变量加1,release 步骤给retainCount变量减1 。当 使用alloc为对象 调配一片内存空间的时候,retainCount会为1 。在这个对象的生命周期内,这个对象可能 接续被其它变量 引用 。但有新的变量指向这个对象的时候,你应该调用retain 步骤,这样运行时才会晓得有新的 引用指向了这个变量,在这个对象生存期中 占有它的 使用权 。这个被Objective-C开发人员称之为“ 占有” 。例如:

  1. Foo * myFooOne = [[Foo alloc] init]; //retaincount 为1 
  2.  
  3. Foo * myFooTwo = myFooOne; //myFooTwo 指向了这个对象 
  4.  
  5. //retaincount  依旧为1 
  6.  
  7. [myFooTwo retain]; //调用retain 步骤,运行时才晓得myFooTwo指向了该对象,retaincount 为2 

  上面的代码中,myFooTwo通过调用retain 步骤, 获得了Foo对象的 占有权 。在这个对象的生命周期中,会有众多变量来指向和 引用它 。指向这个对象的变量也 可以通过release 步骤来解除这种 占有权 。release 步骤将会告诉运行时,我已经 使用完这个变量了,已经不需求它了,retainCount计数减1 。

  当对象的retainCount的计数大于或者等于1的时候,运行时会 接续维持这个对象 。当对象的retainCount为0的时候,运行时会 开释这个对象,并回收它占得内存空间 。

  下图 展示了一个Foo对象的生命周期 。Foo对象首先在内存中 调配一个内存空间,而且被myFooOne 引用 。在这个时候Foo对象的retaincount为1 。

  Foo * myFooOne = [[Foo alloc] init];

Foo对象的生命周期

  第二个 引用变量指向Foo对象,这个 引用变量接着调用retain 步骤,其实也是调用Foo对象的retain 步骤 。Foo对象的retaincount变成2 。

  1. Foo * myFooTwo = myFooOne
  2.  
  3. [myFooTwo retain]; 

  接着当myFooOne 引用不需求的时候,通过调用release 步骤,解除与Foo对象的 占有权,Foo对象的retaincount变成1 。

  1. [myFooOne release]; 

  但myFooTwo不在需求的时候,同样通过调用release 步骤,解除与Foo对象的 占有权,Foo对象的retaincount变成0 。

  内存泄露

  我们 时常会在一个 步骤中申明对象,看下面这个例子:

  1. -(void) myMethod { 
  2.  
  3. //incorrect method 
  4.  
  5. NSString * myString = [[NSString alloc] init]; //retainCount = 1 
  6.  
  7. Foo * myFoo = [[Foo alloc] initWithName:myString]; //retainCount = 1 
  8.  
  9. NSLog(@"Foo's Name:%@", [myFoo getName]); 
  10.  

  这上面这个 步骤中,我们为myString 和myFoo 调配了内存空间 。 步骤执行 完毕之后,两个变量超出了作用域的 规模,所以不再有效 。然而这个 步骤并没有releases这两个对象 。所以运行时没有 开释这两个变量占领的内存空间 。除非你的 利用程序 完毕,不然这两个变量占领的内存空间向来都是不可用的 。我们把它称之为内存泄露 。

  为了 预防内存泄露 。无论什么时候,我们 缔造一个对象,或者 缔造一个对象的拷贝,我们都必须通过release 步骤 开释 。

  1. -(void) myMethod { 
  2.  
  3. NSString * myString = [[NSString alloc] init]; //retainCount=1 
  4.  
  5. Foo * myFoo = [[Foo alloc] initWithName:myString]; //retainCount=1 
  6.  
  7. NSLog("Foo's Name:%@", [myFoo getName]); 
  8.  
  9. [myFoo release]; //retainCount=0 so deallocate 
  10.  
  11. [myString release]; //retainCount=0 so deallocate 
  12.  

  弱 引用

  看下面的例子:

  1. -(void) myMethod { 
  2.  
  3. //an incorrect method 
  4.  
  5. Foo * myFooOne = [[Foo alloc] initWithName:@"James"]; //retainCount=1 
  6.  
  7. Foo * myFooTwo = myFooOne; //retainCount still 1 
  8.  
  9. [myFooOne release]; //retaincount=0 so deallocated 
  10.  
  11. NSLog("Name:%@", [myFooTwo printOutName]); //runtime error 
  12.  

  nyFooTwo指向了Foo对象,然而没有调用retain 步骤,便是一种弱 引用,上面的代码会在运行时报错 。由于myFooOne调用release 步骤 。retaincount变成0,运行时,回收了对象的内存空间 。 而后myFooTwo调用printPutName自然就报错了,见下图 注明 。

运行时报错

  总结:本文 方便的介绍了一下手动的进行内存治理、内存泄露、弱 引用等Objective-C的 常识 。