首页 > C风格编程语言 > Objective-c > Objective-C内存管理基础
2012
02-22

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的知识。


Objective-C内存管理基础》有 0 条评论

  1. coolker 说:

    对于我们.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];

    http://images.51cto.com/files/uploadimg/20110511/1550470.png','Image‘);” onmouseover=”this.style.cursor=’hand’” onmouseout=”this.style.cursor=”” src=”http://images.51cto.com/files/uploadimg/20110511/1550470.png” width=”498″ border=”0″ alt=”Foo对象的生命周期” title=”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自然就报错了,见下图说明。

    http://images.51cto.com/files/uploadimg/20110511/1550471.png','Image‘);” onmouseover=”this.style.cursor=’hand’” onmouseout=”this.style.cursor=”” src=”http://images.51cto.com/files/uploadimg/20110511/1550471.png” width=”498″ border=”0″ alt=”运行时报错” title=”运行时报错” />

    总结:本文简单的介绍了一下手动的进行内存管理、内存泄露、弱引用等Objective-C的知识。

留下一个回复