iOS渐入佳境之内存管理机制(二):ARC

ARC解决的问题(ARC都做了什么)

ARC就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。所以,ARC是编译器(时)特性,而不是运行时特性,更不是垃圾回收器(GC)。

所以,我们需要知道的是,编译时,在哪儿自动加入了retain/release。这都需要修饰符来告诉编译器。所以,我们需要知道的是,编译时,在哪儿自动加入了retain/release。这都需要修饰符来告诉编译器。

修饰符

1.ARC中的变量修饰符

变量修饰符(strong,weak,autoreleasing,unsafe_unretained),主要用来标示对象的生命周期,用来修饰NSObject/id。在MRC中没有这些概念。

表达方法:

类名* 修饰符 变量名(官方要求)

修饰符 类名* 变量名 (文档说是错误的,但是苹果考虑到很多人会用错,所以在编译器这边贴心地帮我们忽略并处理掉了这个错误:)虽然不报错,但是我们还是应该按照正确的方式去使用这些修饰符)

__strong

  1. 变量声明缺省都带有__strong关键字,如果变量什么关键字都不写,那么缺省就是强引用。

  2. 表示引用为强引用,对应MRC在定义property时的”strong”。所有对象只有当没有任何一个强引用指向时,才会被释放。

  3. 当需要释放强引用指向的对象时,需要将强引用置nil。

__weak

  1. 表示引用为弱引用。对应MRC在定义property时用的”weak”。弱引用不会影响对象的释放,即只要对象没有任何强引用指向,即使有100个弱引用对象指向也没用,该对象依然会被释放。

  2. 对象在被释放的同时,指向它的弱引用会自动被置nil。这样能有效防止无效指针、野指针的产生。

  3. 典型应用:__weak一般用在delegate关系中防止循环引用或者用来修饰指向由Interface Builder编辑与生成的UI控件(IBOut)。

__autoreleasing

  1. 表示在autorelease pool中自动释放对象的引用,和MRC时代autorelease的用法相同。

  2. 作用:用于标识id的引用参数(也就是对象的指针的指针),或者需要自动释放的返回的对象(返回值默认都会加上__autoreleasing属性,可以不用写),用来指示通过引用传递的参数。*主要作用是延迟对象释放,使方法内部生成的对象可以在外部访问。

  3. 典型应用:NSError

1
2
3
4
5
NSError *__autoreleasing *error; 
if (![data writeToFile:filename options:NSDataWritingAtomic error:&error])
{
  NSLog(@"Error: %@", error);
}
  • 注意:关于autoreleasing,我们一般很少显式的使用,但是在一些情况下**默认是添加了autoreleasing**修饰的:

(1)作为返回值的对象默认加上了__autoreleasing修饰

在ARC时,编译器会检查方法是否以alloc/new/copy/mutableCopy/init等开头,如果不是且有返回值的话会自己将返回值加入__autoreleasing修饰。所以,ARC有方法命名规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//使用MRC
-(NSString *)stringTest
{
NSString *retStr = [NSString stringWithString:@"test"];

return [[retStr retain] autorelease];
}

// 使用ARC

-(NSString *)stringTest
{
__autoreleasing NSString *retStr = [NSString alloc] initWithString:@"test"];

return retStr;
}

(2)id的指针(指针的指针)在没有显式的指定时会自动加上__autoreleasing修饰。想传一个未初始化的对像引用到一个方法当中,在此方法中实例化此对像(其实就是NSError的例子)

(3)某些类的方法会隐式地使用自己的autorelease pool,在这种时候使用__autoreleasing类型要特别小心。

   这里有详细讲一个例子。

__unsafe_unretained

  1. 跟__weak很像,只是对象在被释放的同时,引用不会自动被置nil。

  2. ARC是在iOS 5引入的,而这个修饰符主要是为了在ARC刚发布时兼容iOS 4以及版本更低的设备,因为这些版本的设备没有weak pointer system。

  3. 现在可以完全忽略掉这个修饰符了,因为iOS 4早已退出历史舞台很多年。

注意事项

  1. ARC中,无修饰符的NSObject对象指针默认是__strong,而无修饰符的id指针(也就是对象的指针的指针,id本身就是指针,好比void )默认的修饰符为*__autoreleasing

  2. ARC中,__strong__weak__autoreleasing修饰的栈变量默认都被初始化为nil。(在ObjC中对象时存储在堆中的,注意基本类型是由系统自己管理的,放在栈上。)

以下代码会输出null而不是crash:)

1
2
3
4
5
- (void)myMethod 
{
NSString *name;
NSLog(@"name: %@", name);
}

ARC中的属性修饰符

除了MRC中的retain,assign,copy,ARC增加了strong,和 weak。相当于是给各属性的类成员变量增加对应的所有权修饰符。如strong对应的是__strong。其他的就不赘述了。

对象处理的基本规则

  1. 从以上的分析可以看到,虽然ARC在本质上也是采用了引用计数的概念,但作为开发者来讲,ARC环境基本上很少考虑非ARC环境中的复杂的引用计数关系。在ARC中,只需要明确强引用和弱引用的概念,其他的问题编译器自动帮助我们完成了。

  2. 当超过了对象的作用域以及没有强引用时,对象会自动销毁。

版本问题

引入时间: iOS 5/ Mac OS X 10.7,Xcode4.2 ,所以需要注意哪些版本是支持的。

ARC技术和跟随Xcode4.2一起发布的,在缺省的工程模板里可以选择是否支持ARC技术。随着 iOS 5.1 的推出,Xcode也推出了4.3版本。在该版本下,ARC 有效时的属性(@property) 定义的时候,如果不明确指定所有权关键字,那么缺省的就是 strong。而在 Xcode4.2 中,即使 strong 也要显示指定。

基本的ARC使用规则:

  • 代码中不能使用retain, release, retain, autorelease。 不可以再显示调用dealloc、或实现调用retain、release、retainCount、autorelease这些方法。也不能使用@selector(retain), @selector(release),等等。
  • 不重载dealloc(如果是释放对象内存以外的处理,是可以重载该函数的,但是不能调用[super dealloc]),因为super的调用是由编译器自动强制执行的。
  • 不能使用NSAllocateObject, NSDeallocateObject。使用alloc来创建对象,由ARC来管理对象运行时的释放。
  • 不能在C结构体中使用对象指针。建议使用Objective-C的class来管理数据格式,来代替C语言的struct。
  • id与void *间的如果cast时需要用特定的方法(__bridge关键字)
  • 不能使用NSAutoReleasePool、而需要@autoreleasepool块
  • ARC的方法命名规则对象:生成和持有的方法必须是alloc,new,copy,mutableCopy等开头的方法(ARC和非ARC都是这样),特别的init开头的方法必须是实例方法,且必须要返回对象。基于这个规则,访问器方法不能已new,init(注意initiaize这样的不属于这个规则之内的,initMyObject这样的才算),copy等开头。比如你不能声明一个已new开头的属性,除非你给你指定一个getter。内部参数没有这个限制 。