自动引用计数器(ARC)

前言

​ 自动引用计数器是指内存管理对引用采取自动技术的技术。

在LLVM编译器中设置ARC为有效状态,就无数再次键入retain或者release代码。

引用计数

引用计数式内存管理的思考方式:

  • 自己生成的对象,自己持有。
  • 非自己生成的对象,自己也能持有。
  • 不在需要自己持有的对象时释放。
  • 非自己持有的对象无法释放。

在内存管理中主要会对应如下四个操作:生成,持有,释放,废弃。

对象操作 Object-C方法
生成并持有对象 alloc/new/copy/mutableCopy 方法
持有对象 retain 方法
释放对象 release 方法
废弃对象 dealloc 方法

生成并持有对象

1.调用方生成并持有对象

1
2
3
4
- (instancetype)allocObject {
id obj = [[NSObject alloc] init];
return obj;
}

2.调用方生成但不持有对象

1
2
3
4
5
- (instancetype)object {
id obj = [[NSObject alloc] init];
[obj autorelease];
return obj;
}

所以,在日常使用 NSMutableArray类的array方法等就可以获得谁都不持有的对象,这些方法都是通过autorelease来实现的。

自动引用计数器规则

规则

ARC的约定

为什么不能申明属性名为newString

使用ARC之后一个费解的地方是,一个方法生成的对象,没有任何附加标示,ARC怎么知道生成的对象是不是autorelease的呢?

1
2
3
4
@interface Sark : NSObject
+ (instancetype)sarkWithMark:(NSString *)mark; // 1
- (instancetype)initWithMark:(NSString *)mark; // 2
@end

这是非ARC时常用的手段,1生成autorelease对象,2生成普通对象,而现在ARC不能调用autorelease,使用时怎么能知道呢?

1
2
3
4
5
{
// ...
Sark *sark1 = [Sark sarkWithMark:@"萨萨萨"];
Sark *sark2 = [[Sark alloc] initWithMark:@"萨萨萨"];
}

使用 “约定”,NS定义了下面三个编译属性

1
2
3
#define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
#define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained))
#define NS_RETURNS_INNER_POINTER __attribute__((objc_returns_inner_pointer))

这三个属性是Clang自己使用的标示,除非特殊情况不要自己使用,但是这些对理解ARC是很有帮助的。
这里还要介绍一个概念,Method family

An Objective-C method may fall into a method family, which is a conventional set of behaviors ascribed to it by the Cocoa conventions.

指的是命名上表示一类型的方法,比如- init- initWithMark:都属于init的family
于是乎,编译器约定,对于alloc,init,copy,mutableCopy,new这几个家族的方法,后面默认加NS_RETURNS_RETAINED标识;而其他不指名标识的family的方法默认添加NS_RETURNS_NOT_RETAINED标识
也就是说刚才的方法,在编译器看来是这样的:

1
2
3
4
@interface Sark : NSObject
+ (instancetype)sarkWithMark:(NSString *)mark NS_RETURNS_NOT_RETAINED; // 1
- (instancetype)initWithMark:(NSString *)mark NS_RETURNS_RETAINED; // 2
@end

这也就是为什么ARC下面,不能把一个属性定义成名字是这样的:

1
@property (nonatomic, copy) NSString *newString; // 编译器不允许

- newString就成了new家族的方法,内存就不对了
对于NS_RETURNS_INNER_POINTER这货,主要使用在返回的是一个对象的内部C指针的情况,如NSString的方法:

1
- (__strong const char *)UTF8String NS_RETURNS_INNER_POINTER;

就使用了这个标识,这个就不深入研究了,直接上文档:

An Objective-C method returning a non-retainable pointer may be annotated with the objc_returns_inner_pointer attribute to indicate that it returns a handle to the internal data of an object, and that this reference will be invalidated if the object is destroyed. When such a message is sent to an object, the object’s lifetime will be extended until at least the earliest of:
the last use of the returned pointer, or any pointer derived from it, in the calling function or
the autorelease pool is restored to a previous state.

自动引用计数器实现

​ 通过__strong修饰符,不必再次键入retainrelease。使得ARC可以有效并简单的编程遵循Object-C内存管理的思考方式。

​ “自己生成的对象,自己持有”和’’非自己生成的对象,自己也能持有’’只需要通过对带__stong修饰符的变量赋值便可以达成。通过废弃带__strong修饰符的变量(变量作用域结束或是成员变量所属对象废弃)或者对变量赋值,都可以做到’’不在需要自己持有的对象时释放。’’非自己持有的对象无法释放’’,由于不必再次键入release,所以原本就不会执行。