30、以ARC简化引用计数

​ Clang编译器项目带有一个“静态分析器”(static analyzer),用于指明程序里引用计数出问题的地方。

​ 方法命名规范:

  1. alloc、new、copy、mutableCopy开头的方法内部会调用retain,使引用计数+1,方便调用者持有返回的对象,调用者也需要负责释放该对象。

  2. 其他开头的方法内部会调用autorelease,使对象在跨越方法调用边界后依然有效,但过段时间会被释放。要想令调用者持有它,需要执行保留方法才行。

维系这些规则所需的全部内存管理事宜均由ARC自动处理,如:

// 以new开头,调用者需要持有对象;所以其引用计数需要+1
+ (Person *)newPerson {
    Person *person = [[Person alloc] init]; // alloc开头:引用计数已经+1
  return person;
} // 所以ARC什么代码都不需要加

// 不是以“owning prefixs”开头,调用者不需要持有对象;所以其引用计数不需要+1
+ (Person *)somePerson {
    Person *person = [[Person alloc] init]; // alloc开头:引用计数已经+1
  return person;
  // 所以ARC会在改为类似 return [person autorelease]; 的代码
} 

+ (Person *)doSomething {
    Person *personOne = [Person newPerson];
  Person *personTwo = [Person somePerson]; // autorelease
  // 所以ARC会添加类似  [personOne release]; 的代码
}

​ ARC会把能互相抵消的retain、release、autorelease操作约简。如在发现同一个对象上执行了多次“保留”与“释放”操作,则可以成对移除这两操作。如:

Person *tmp = [Person personWithName:@"momo"];
_myPerson = [tmp retain];
// personWithName: 里的autorelease和后面的retain都是多余的。
// 为了提升性能皆可省去

​ 为了优化代码,在方法中返回自动释放的对象时,调用的是objc_autoreleaseReturnValue,此函数会检视当前方法返回之后即将要执行的那段代码,若发现那段代码要在返回的对象上执行retain操作,则会根据当前对象在全局数据结构中设置一个标志位,并不执行autorelease操作。相似的保留对象调用的是objc_retainAutoreleaseReturnValue函数,此函数会根据当前对象到全局数据结构中找到刚才那个标志位,若已置位,则不执行retain操作。(设置并检测标志位,要比调用autoreleaseretain更快)例如:

// objc_xxx:直接调用C函数,不经过OC的消息派发,速度更快
+ (Person *)personWithName:(NSSrting *)name {
  Person *person = [[Person alloc] init];
  person.name = name;
  objc_autoreleaseReturnValue(person); //
}
Person *tmp = [Person personWithName:@"Momo"];
_myPerson = objc_retainAutoreleaseReturnValue(tmp); //

要点:

  • 有ARC后,程序员无须担心内存管理问题,可省去类中的许多“样板代码”

  • ARC管理对象生命期的办法基本上就是:在合适的地方插入“保留”及“释放”操作。在ARC环境下,变量的内存管理语义可以通过修饰符指明,而原来则需要手工执行“保留”及“释放”操作

  • 由方法返回的对象,其内存管理语义是通过方法名来体现。ARC将此确定为开发者必须遵守的规则

  • ARC只负责管理OC对象的内存。尤其要注意:CoreFoundation对象不归ARC管理,开发者必须适时调用CFRetain/CFRelease。

Last updated

Was this helpful?