Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2389325
  • 博文数量: 877
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 5920
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-05 12:25
个人简介

技术的乐趣在于分享,欢迎多多交流,多多沟通。

文章分类

全部博文(877)

文章存档

2021年(2)

2016年(20)

2015年(471)

2014年(358)

2013年(26)

分类: iOS平台

2015-08-06 14:21:21

Objective-C 内存管理
http://blog.csdn.net/ztp800201/article/details/7794343
C语言使用malloc方法申请内存,使用free释放内存。

Objective-c使用alloc方法申请内存,使用Release来释放它。


一、内存申请(alloc)
    当使用alloc创建一个对象时,需要在用完之个对象后释放(Release)它。
    比如:


  1. //str1会自动释放,我们不需要自己去释放  
  2. NSString* str1 = [NSString string];  
  3.   
  4. //str2需要手工释放  
  5. NSString* str2 = [[NSString alloc] init];  
  6. ...  
  7. [str release];  //用release释放str2的内存  


二、释放内存(dealloc)
    当一个对象从内存上删除之前,系统就会自动调用dealloc方法,释放内存。
    比如:



  1. -(void)dealloc {  
  2.   [name release];  
  3.   [var release];  
  4.   [super dealloc];  //这一句一定不要少了  
  5. }  

dealloc是由系统自动调用的,我们永远都不要自己主动去调用dealloc来释放内存。
不过,我们可以像上面那样重载dealloc方法,来释放自己的内存及清理的相关工作。
在垃圾回收机制下,dealloc不会被调用到。



三、引用记数(retainCount)

整个Objective-c中都使用对象引用,而且每个对象有一个引用计数器retainCount
当使用alloc(或者copy)方法创建一个对象时,其计数器的值增加1。
调用retain方法增加1,调用release方法就减少1。当计数器为0时,系统自动调用dealloc方法来释放内存中的对象。
如:


  1. NSString* str = [[NSString alloc] init];  //执行后,retainCount的值为1  
  2. [str retain];  //执行后,retainCount的值为2  
  3. [str release]; //执行后,retainCount的值为1  
  4. [str release]; //执行后,retainCount的值为0  


四、类成员变量

在大多数情况下,一个成员变量的setter方法应该仅仅autorelease/release旧对象,然后retain/copy新的对象,我们只需要在dealloc的时候调用release就可以了
所以真正需要做的就是管理方法内部的局部变量的引用。如:


  1. -(void)setName:(NSString*)newName {  
  2.   if(name != newName ) {  
  3.      [name release];  
  4.      name = [newName copy];  // name的retainCount为1  
  5.   }  
  6. }  
下面是使用retain管理成员变量的另外一个例子:



  1. -(void)setName:(NSString*)newName {  
  2.   [name autorelease];  
  3.   name = [newName retain];  // name的retainCount为1  
  4. }  
  5.   
  6. -(void) dealloc {  
  7.   [name release];  
  8.   [super dealloc];  
  9. }  


在上面的dealloc函数,如果写成下面的形式也是可以的:


  1. -(void) dealloc {  
  2.   self.name = nil;  
  3.   [super dealloc];  
  4. }  

前提是name变量具有@property和@synthesize的属性,为什么这样写也可以?是因为当我们将nil赋值给一个实例变量,那么它是调用了设置器的函数setName,设置器(setter)会释放旧对象并且保留(retain)nil对象。这种做法对于dealloc来说更好一些,因为这样做避免了让变量指向一个随机的数据,而这个数据又恰好是另外一个对象。


如果写成下面的形式的话,就会产生内存泄露:


  1. -(void) dealloc {  
  2.   name = nil;  //错误,变量没有释放,会产生内存泄露  
  3.   [super dealloc];  
  4. }  



五、自动释放池 (autorelease)


创建自动释放池:


  1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];  

在池创建之后,pool释放之前所有Autorelease属性的数组、字符串都将放入这个池中。
只要一个对象被标志为自动释放,那就会被放到自动释放池中。
当下面的语句被执行时,池中对象的内存就被释放:



  1. [pool drain];  

释放自动释放池:



  1. [pool release];  

可以看出自动释放池跟任何其它普通对象没任何区别。

NSAutoreleasePool内部包含一个数组(NSMutableArray),用来保存声明为autorelease的所有对象。如果一个对象声明为autorelease,系统所做的工作就是把这个对象加入到这个数组中去。

它的作用和精髓就在于在声明pool后,release它之前的这段代码,所有段里的代码(先假设中间没有声明其它的AutoreleasePool实例),凡是调用了autorelase方法的实例,都会把它的retainCount加1,并在此pool实例中添1次此实例要回收的记录以做备案。当此pool实例dealloc时,首先会检查之前备案的所有实例,所有记录在案的实例都会依次调用它的release方法。如果此时数组中成员的retain count大于1,那么release之后,retain count大于0,此对象依然没有被销毁,内存泄露。


在代码中使用alloc的变量也是可以使用autorelease的,如:


  1. -(NSString*) welcome {  
  2.   NSString* result;  
  3.   result = [[NSString alloc] init];  
  4.   [result autorelease]; //正确。在AutoreleasePool栈顶的autoreleasePool实例添加此对象  
  5.   return result;  
  6. }  

也可以写成:



  1. -(NSString*) welcome {  
  2.   NSString* result;  
  3.   result = [[[NSString alloc] init] autorelease];  
  4.   return result;  
  5. }  



通过使用autorelease,该对象就被放入自动释放池(autorelease),系统自动跟踪每个对象的使用情况,在释放自动释放池时,释放池中的所有对象。

另外需要注意的是,autorelease不是系统的垃圾收集(Garbage Collection)功能. 在iPad/iPhone中没有垃圾收集功能。



六、垃圾回收 (Garbage-collection)
垃圾回收(Gargage-collection)是Objective-c提供的一种自动内存回收机制。在iPad/iPhone环境中不支持垃圾回收功能。
当启动这个功能后,所有的retain,autorelease,release和dealloc方法都将被系统忽略。


七、copy, nonatomic
对于字符串类型的属性变量,我们经常使用下面的类似的语句:


  1. @property (nonatomic, copy) NSString* name;  

它等价于:



  1. -(void) setName:(NSString*) newName {  
  2.    if(newName != name) {  
  3.      [name release];  
  4.      name = [newName copy];  
  5.     }  
  6. }  

那么为什么使用copy呢?如果直接使用下面的语句,结果如何:



  1. -(void) setName: (NSString*) newName {  
  2.   name = newName;  
  3. }  

这样的结果是name和newName指向同一个对象,如果newName的值发生变化的话,name的值也发生变化,这显然不是我们想要的结果。
使用copy,其完成的功能就是调用一个alloc方法来创建一个新的字符串对象(使用initWithString:newName)
nonatomic的意思是不需要使用互斥锁。
atomic是使用互斥锁,缺省选项是atomic。
如果你的程序并没有使用多钱程,可以将互斥锁设置为nonatomic.



八、内存管理的基本原则

1、如果使用alloc(或者copy)方法创建一个对象,或者使用retain保留一个对象,那么就要自己释放对象。
   (1)用allco(或copy)时,用release释放。
   (2)用retain时,用autorelase增加到自动释放池中。
2、申请内存的语句数量和释放内存的语句数量应该相等。


---------------------------------------------下面是从网上转载的---------------------------------------------------------------------------------------


1           口诀。

1.1          谁创建,谁释放(类似于“谁污染,谁治理”)。如果你通过allocnewcopy来创建一个对象,那么你必须调用releaseautorelease。换句话说,不是你创建的,就不用你去释放。
例如,你在一个函数中alloc生成了一个对象,且这个对象只在这个函数中被使用,那么你必须在这个函数中调用releaseautorelease。如果你在一个class的某个方法中alloc一个成员对象,且没有调用autorelease,那么你需要在这个类的dealloc方法中调用release;如果调用了autorelease,那么在dealloc方法中什么都不需要做。

1.2          除了allocnewcopy之外的方法创建的对象都被声明了autorelease

1.3          retain,谁release。只要你调用了retain,无论这个对象是如何生成的,你都要调用release。有时候你的代码中明明没有retain,可是系统会在默认实现中加入retain。不知道为什么苹果公司的文档没有强调这个非常重要的一点,请参考范式2.7和第三章。

下面给出原文链接:

iPhone/Mac Objective-C内存管理教程和原理剖析(一)基本原理

iPhone/Mac Objective-C内存管理教程和原理剖析(二)口诀与范式

原作者写不错,通俗易懂,值得一读,推荐一下,对初学者来说,是有帮助的。

阅读(274) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~