理解ARC
Clang编译器项目带有一个“静态分析器”,用以指明程序里引用计数出问题的地方,查明内存管理问题,预先加入适当的保留或释放操作以避免这些问题。自动引用计数思路正是源于此。自动引用计数所做的事情与其名称相符,就是自动管理引用计数。
使用ARC时引用计数实际上还是要执行的,只不过保留与释放操作现在是由ARC自动为你添加。ARC会自动执行retain、release、autorelease等操作,所以直接在ARC下调用这些内存管理方法是非法的。实际上ARC在调用这些方法时,并不通过普通的消息派发机制,而是直接调用其底层C语言版本。这样做性能更好,因为保留及释放操作需要频繁执行,所以直接调用底层函数能节省很多CPU周期。比如说会调用鱼retain等价的底层函数objc_retain。
ARC还可以执行一些手工操作很难甚至无法完成的优化,在编译器ARC会把能够互相抵消的retain、release、autorelease操作约简。如果发现在同一个对象上执行了多次“保留”与“释放”操作,那么ARC有时可以成对的移除这两个操作。
ARC包含了运行期组件,在运行期可以检测到这一对多余的操作,也就是autorelease及紧跟其后的retain。为了优化代码,在方法中返回自动释放的对象时,要执行一个特殊函数。此时不直接调用对象的autorelease方法,而是改为调用objc_autoreleaseReturnValue。此函数会检视当前方法返回之后即将执行的代码,若发现那段代码在返回的对象上调用了retain操作,则设置全局数据结构(此数据结构的具体内容因处理器而异)中的一个标志位,而不执行autorelease操作。与之相似,如果方法返回了一个自动释放的对象,而调用方法的对象要保留此对象,那么此时不直接执行retain,而是改为执行objc_retainAutoreleaseReturnValue函数,此函数要检测刚才提到的那个标志位,若已经置位,则不执行retain操作。设置并检测标志位要比调用autorelease和retain更快。
例:
+ (EOCPerson*)personWithName:(NSString *)name {
EOCPerson *person = [[EOCPerson alloc] init];
person.name = name;
objc_autoreleaseReturnValue(person);
}
EOCPerson *tmp = [EOCPerson personWithName:@"wangkai"];
_myPerson = objc_retainAutoreleasedReturnValue(tmp);
在非ARC情况下编写设置方法我们通常会这样做:
- (void)setObject:(id)object {
[_object release];
_object = [object retain];
}
实际上这样做会有明显的问题,假如新值和实例变量已有的值相同,那么会导致该值被系统回收,接下来再执行保留操作,就会令应用程序崩溃。在ARC的情况下可以这么写:
- (void)setObject:(id)object {
_object = object;
}
ARC会用一种安全的方式来设置,先保留新值再释放旧值,最后设置实例变量。
总结:
1)ARC在运行期时自动添加内存管理方法。
2)ARC内存管理方法的调用并非是消息派发机制,而是直接调用底层的C语言函数。
3)ARC会成对的抵消retain、release、autorelease操作。
4)ARC会用一种安全的方式来,先保留新值再释放旧值,最后设置实例变量。
5)ARC管理生命周期在合适的地方插入“保留”及“释放”操作,变量内存管理语义可以通过修饰符表明,而原来则需要手工执行“保留”及“释放”操作。
6)ARC只负责管理Objective-C对象内存,CoreFoundation对象不归ARC管理,开发者必须适时调用CFRetain/CFRelease。