理解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。

posted on 2018-02-28 16:05  jack.xi  阅读(465)  评论(0)    收藏  举报

导航