ARC 简介

从 iOS 5开始,OC 引进了一种 ARC(Automatic Reference Counting)的方式来管理内存,由编译器来为我们生成相应的内存管理代码。

在类属性的声明中,ARC 相关的修饰词有 4 个:

  • strong 一个对象只要存在一个 strong 类型的指针指向它,就不会被释放。

  • weak 不会延长对象的生命周期,当 weak 类型的指针指向的对象被释放时,指针自身会被自动置为 nil

  • assign 用于 C 原始类型,比如 int。

  • copy 传入对象时会浅拷贝对象。

  • 一般将不可变的 property 设置为 copy,当可变的对象传入时,编译器会自动拷贝一份不可变的版本,以保证其内容不会被修改。如果传入的不可变对象,会增加其引用计数。

  • 当 property 是一个 block 时通常使用 copy。因为 block 需要在其所在的 scope 之外保留其捕获的状态。不声明也是可以的,但官方推荐这样使用,以提醒代码阅读者。

在 ARC 机制下,指针缺省的类型是 strong

在声明局部变量时,有四个 ARC 相关的修饰词,他们的用法跟 const 类似。

  • __strong 缺省值。用法同 strong

  • __weak 用法同 weak

  • __unsafe_unretained 类似于 weak,但其指向的对象被释放时,其值不会被置为 nil。应该是由于兼容性的原因而存在,通常不建议使用。

  • __autoreleasing 用来修饰一个以引用(id *)方式传入的参数,当函数返回值时被释放。

上面的修饰词正确的使用方法是

  1. ClassName * qualifier variableName;

修饰词放在其他位置也可以编译通过,但是严格来说是不正确的。

循环引用以及避免方法

当两个对象使用 strong 指针互相引用时,就会出现双方都不会被释放的情况,经常出现在使用 block 的地方。

例如下面的代码就存在循环引用:

  1. @property (strong) void(^someBlock)();
  2. [self setSomeBlock:^{
  3. [self doSomething];
  4. }];

在属性的生命中,类的对象含有一个 strong 类型的指针指向 block,在 block 中又存在 self (默认是 strong 类型)来引用类的对象,这样两者在生命周期结束时都不会被释放。

解决方法:通过使用__weak 修饰词来得到一个指向 self 的 weak 指针来打破循环引用。

  1. @property (strong) void(^someBlock)();
  2. MyClass *__weak weakSelf = self;
  3. [self setSomeBlock:^{
  4. MyClass *__strong strongSelf = weakSelf;
  5. [strongSelf doSomething];
  6. }];

block 内部的 strong 指针是为了保证在 block 执行的过程中,对象不会被释放掉。其实也可以先判断一下 weakSelf 是否为空。

但不应在所有使用 block 的情况下都采用 weakSelf,因为只要类中不存在指向 block 的 strong 指针,就不会存在循环引用。而滥用weakSelf 可能会导致某些情况下 self 被释放后才调用 block,此时 block 中的代码不会被执行,往往不是我们期望的。