OC 中的 ARC

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
2
3
4
5
@property (strong) void(^someBlock)();

[self setSomeBlock:^{
[self doSomething];
}];

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

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

1
2
3
4
5
6
7
@property (strong) void(^someBlock)();

MyClass *__weak weakSelf = self;
[self setSomeBlock:^{
MyClass *__strong strongSelf = weakSelf;
[strongSelf doSomething];
}];

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

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

给鸡排饭加个蛋