Object-c内存管理方式有三种:手动引用计数(MRC,Manual Reference Count)、自动引用计数(ARC,Automatic Reference Count)、自动垃圾回收。
1.MRC是默认的内存管理方式。就使用的难易度上说还是较难的,稍不注意可能就导致了内存泄漏(什么是内存泄漏呢,内存泄漏是指程序未能释放不再使用的内存),如alloc和release没有成对使用(常见的现象是使用循环时用了break、goto、continue在一定条件下跳出循环,而release语句在break、goto、continue之后,导致没有执行release,从而出现内存泄漏);
2.ARC在IOS5之后被强烈建议使用。为什么呢?因为编译器会根据传入变量是引用变量还是局部变量,返回对象的方法是不是初始化方法等信息来推断何处应该自动添加autorelease/retain/release,不需要程序员手动添加,从而使程序员有更多时间去开发程序的核心功能。
3.自动垃圾回收是IOS不支持的(Mac支持)。今天在这里就不暂时介绍了;
那引用计数的基本思想或者说基本原理是什么呢?
首先,我们创建一个对象会执行alloc,为这个对象分配一个内存空间。诶,既然分配了空间,我们自然要在程序执行完毕时释放这个内存空间,否则就造成内存泄漏了。那怎么让系统知道要在这时候释放这个实例对象的内存空间呢?采用标记咯,则这个引用计数我就把他理解为一个“标记符”。
我们在执行alloc和初始化方法是,引用计数就会+1(从0变为1)。此时系统分配了内存,“标记符”就不是零了。假设一个对象A会调用对象B,为了不让B随意释放,A就需要执行retain操作,使B的引用计数+1,宣布A是B的拥有者。即告诉系统,对象B是我的,你不可以随意释放。在A使用完B之后,A向B发送release消息,表示宣布B不再是我的人了,此时B的引用计数就会-1,如果刚好这个值-1=0,那么B就会被释放了。注意:同一个对象可以有多个拥有者,每个拥有者宣布拥有他时都必须要执行retain。这样可以保证这个对象在另一个对象需要调用它之前不被系统释放。
我们如果想要知道这个实际的引用计数值是多少,就会用到retainCount方法。示例代码如下:
#import <Foundation /NSObject.h>
#import<stdio.h>
int main(){
id obj=[[NSObject alloc] init];
printf("执行初始化后,引用计数为:%d\n",(int) [obj retainCount]);
[obj retain];
printf("执行retain后,引用计数为:%d\n",(int) [obj retainCount]);
[obj release];
printf("执行release后,引用计数为:%d\n",(int) [obj retainCount]);
[obj release];
return 0;
}
执行完后输出结果是:
执行初始化后,引用计数为:1
执行retain后,引用计数为:2
执行release后,引用计数为:1
注意:alloc、init与release,retain与release必须要成对出现。
引用计数为0时,系统会自动调用dealloc方法,释放内存。通常程序内是不允许直接调用这个方法的。
嗯,现在我们基本清楚了引用计数内存管理的基本思想,而这种基本思想必然是会扩展的。实际编写程序时,我们会遇到很多只需要使用一次的对象,而逐一的释放这些对象会显得很麻烦。于是呢,Cocoa环境的Object-C提供了一种自动释放机制。这个机制是怎样的呢?从功能上来认识,它要实现的目的很简单,就是将这些使用次数少且使用完后的对象统一的释放。那么很显然,它首先要做的就是把这些对象集中起来,而集中的容器就应运而生——AtuoreleasePool(自动释放池)。同样使用标记的方式,用类NSAutoreleasePool来记录。这种机制的使用方法如下:
1.创建一个NSAutoreleasePool的实例对象;
2.当这个对象需要被释放时用autorelease替代release,将这个对象标记(标记为以后释放)后放入自动释放池;
3.销毁自动释放池,池中记录的所有对象都被发送release消息,然后就被统一释放了。
这里需要注意的是,autorelease方法虽然不会使对象的引用计数发生变化,但是它和release一样都宣布放弃对象所有权。autorelease也要和retain成对出现。
这些被记录到自动释放池中的对象称为临时对象。这种对象可以直接创建,在创建后就会被直接加入到内部的自动释放池。如+(id) stringWithUTF8String:(const char *) bytes,
这就是个生成临时变量的类方法(不以init开头而已生成对象类型作为开头的方法),O-C中它就叫做便利构造函数。以上都是手动引用计数内存管理的方法。
手动管理自动释放池使用方法如下,
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
/*进行一系列操作*/
/*这里不可以使用break、continue、goto语句*/
[pool release];
自动管理自动释放池,代码变为:
@autoreleasepool{
/*进行一系列操作*/
/*这里可以使用break、continue、goto语句*/
}
为什么后一种方法可以使用break、continue等语句呢?因为程序运动到@autoreleasepool块外才会进行对象释放,所以你在块内怎么continue,怎么goto都没关系。我想这也是为什么在ios5之后,强烈推荐使用ARC机制。采用ARC必定是让我们程序员少了很多工作,但使用它时也有以下几点注意事项:
1.使用ARC内存管理时,不能在程序中添加retain、release、autorelease和retainCount;
2.要使用@autoreleasepoo代替NSAutoreleasePool;
3.方法命名必须依照命名规则,不能随意定义以new/copy/alloc/init/mutablecopy开头且和所有权操作无关的方法;
4.不用再dealloc中释放实例变量,也不需要调用[super dealloc];
5.编译时使用clang编译器,并加上编译选项-fobjc-arc;
最后说一点,在Xcode中,我们可以在菜单中选中Edit-Refactor,找到一个叫Convert to Object-C ARC的小工具。这个工具可以将代码转化为支持ARC的代码。但垃圾回收的代码不能被自动转换。