Objective-C 的内存管理方法,分为3种
<br />
MRC(Manual Reference Counting, 手动引用计数)
ARC(Automatic Reference Counting, 手动引用计数)
GC (Garbage Collector, 垃圾回收机制)
<br />
MRC:就是由程序员自己负责管理对象生命周期,负责对象的创建和销毁。
<br />
ARC:采用与MRC一样的内存引用计数管理方法,但不同的是,它在编译时会在合适的位置插入对象内存释放(如release、autorelease和retain等),程序员不用关心对象释放的问题。苹果推荐在项目中使用ARC。ARC不能在iOS5之前的系统中使用。
<br />
GC:在OC2.0之后,内存管理出现了类似于JAVA和C#的内存垃圾收集技术,但与ARC完全不同,GC是后台有一个线程负责监察已经不再使用的对象,然后将它释放。由于后台一直有一个线程在运行,因此会严重影响性能,这也是Android(安卓)手机一直让人不爽的原因。重点!CG技术不能应用于iOS开发,只能应用于Mac OS X 开发。
<br />
由上可知:iOS采用ARC和MRC这两种方式,ARC是苹果推荐的方式,MRC相对比较原始,对于程序员的能力要求很高,但是它很灵活、方便,很不容易驾驭。另外,Swift采用ARC管理内存。
<br />
1)与retain配对使用的方法是release,因为retain是将内存的引用计数加一(对对象进行一次持有),release是将内存的引用计数减一(结束对对象的持有)。
2)与alloc对应的方法是dealloc,因为alloc表示开辟内存空间,创建对象,dealloc表示回收内存空间,释放对象。
3)readwrite表示读写设置中的可读可写(读写设置的默认值),编译器会为读写设置声明为”readwrite”的属性自动合成setter和getter方法。
4)readonly表示读写设置中的只读,编译器会为读写设置声明为”readonly“的属性自动合成”getter”方法,而不合成”setter“方法。
5)assign是语义设置(语义设置的默认值),使用assign修饰的属性所对应的读写方法中不会操作内存的引用计数,所以一般将不需要程序员管理的数据类型声明为assign,如基本数据类型和结构体类型。
6)retain是语义设置,使用retain修饰的属性所对应的读写方法中会对内存的引用计数进行操作,以保证持有的对象不会被意外回收,OC中的对象使用retain修饰。
7)copy是语义设置,使用copy修饰的属性所对应的读写方法中会将将要持有的对象拷贝一份,并持有对象的副本而不是原对象。对于OC中的可变对象,使用copy修饰,是”深拷贝“,在内存中生成一块新的内存空间,将原对象拷贝到新空间中;对于OC中的不可变对象,使用copy修饰,是”浅拷贝“,只拷贝对象的内存地址。
8)atomic:是原子特性(原子设置的默认值),在多线程环境下,使用atomic会保证在同一时刻只有一条线程修改、访问对应的实例变量,保证了实例变量在多线程环境下的安全性,但是比较耗费资源,所以只有对于需要线程安全的属性才使用atomic来修饰。
9)nonatomic:是原子特性,在多线程环境下不能保证线程安全,这种设置不会消耗过多的资源,所以不需要保证多线程环境中线程安全的属性,声明为nonatomic。
10)strong:ARC中的语义设置,相当于MRC中的retain,会操作内存的引用计数。
11)week:ARC中的语义设置,相当于MRC中的assign,不仅不会操作内存的引用计数,而且在对象被回收后,会将指针置为nil,防止”野指针“的出现。
<br />
autorelease的使用问题
<br />
在MRC中,释放对象通过release或autorelease消息实现,其中release消息会立刻使引用计数减一,autorelease消息会使对象放入内存释放池中延迟释放,对象的引用计数并不变化,而是向内存释放池中添加一条记录,直到池被销毁前通知池中的所有对象全部发送release消息才真正将引用计数减少。
<br />
由于使用autorelease消息会使对象延迟释放,所以除非必须的情况,否则不要使用它释放对象。在iOS程序中,内存释放池的释放默认在程序结束。
<br />
我们可以在应用程序的主入口 main.m 中看到这段代码:
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
在@autoreleasepool{...}之间的代码,就是池的作用范围,默认是整个应用。如果产生大量对象,采用autorelease释放也会导致内存泄漏。
<br />
什么时候使用autorelease呢?请看下面的代码:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = @"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: CellIdentifier] autorelease];
}
return cell;
}
在上面的代码中,cell对象不能马上释放,我们需要使用它设置表示图的界面。autorelease一般用在为其他调用者提供对象的方法中,对象在该方法中不能马上释放,所以需要延迟释放。