ARC 简明参考手册 (Part 1)
引言
在早期使用 Objective C 编程的时候, 需要手动管理内存, 对于初学者来说, 这是容易出错的。 后来苹果在 Mac 平台上引入了垃圾收集器的机制, 然而相对于 IOS 来说, 苹果认为垃圾收集会影响移动设备性能, 于是提出了一个跨平台的解决方案, ARC。 ARC 的原理是由编译器推导, 应该在代码中什么位置加入合适的语句来管理内存, 这个过程是在编译阶段进行的, 所以不会影响运行时性能。 现在 ARC已然是苹果标准, 真可谓是对业界使用垃圾收集这个普遍标准的一次颠覆。
一个简单的例子
ARC 不是万能的, 它引入了一些隐形规则(这些规则会和我们的直觉有一定的冲突), 我们需要了解这些规则, 因为我们在某些特别的情况下要干涉; 同时我们也要避免一些陷阱,这些陷阱其实是由于 ARC 的规则造成的坑。 也就是说, ARC有一定的学习曲线, 不像垃圾回收机制一样, "it just works"。
我们先来看一个简单的例子, 下面相似的两个代码段在 ARC 模式下编译运行时没有问题的, 但是在非 ARC 模式下运行的时候就会在 NSLog(@"count: %d", hello.val);
这里抛出 BAD_ACCESS 的错误。 这是为什么呢?
ARC
MyClass *myclass() {
MyClass* hello = [[MyClass alloc] init];
return hello;
}
int main(int argc, const char * argv[]) {
MyClass *hello;
@autoreleasepool {
hello = myclass();
}
NSLog(@"count: %d", hello.val);
return 0;
}
非ARC
MyClass *myclass() {
MyClass* hello = [[MyClass alloc] init];
return [hello autorelease];
}
int main(int argc, const char * argv[]) {
MyClass *hello;
@autoreleasepool {
hello = myclass();
NSLog(@"reference count: %lu", [hello retainCount]);
}
NSLog(@"reference count: %lu", [hello retainCount]);
NSLog(@"count: %d", hello.val);
return 0;
}
因为在 ARC 之前, local variable 是弱引用关系, 而在 ARC 之后则是强引用关系。 一开始会觉得很别扭, 因为指针给我们的印象就应该是弱引用, 不过话说回来, 我们不应该先入为主的认为 Objective C 的 *
和 C/C++ 的指针有半毛钱关系了 , 它就是代表一个引用, 就如同 Java/C# 中的引用。
所以大部分情况下, 我们就像 Java/C# 中那样直接 "it just works" 的愉快的码字就可以了, 无论是在 property 还是 local variable 的情况下, 默认都是强引用。
but (这样的故事通常都会有一个 but), 由于 Objective C 是基于 Reference counting 的, 而 Java/C# 是基于 Tracing garbage collector 的, 这个本质上的区别, 正是导致我们需要在写 Objective C 代码的 时候不能开启全自动导航模式的原因。
从 Garbage collectors 说起
<a href=http://en.wikipedia.org/wiki/Garbage_collection_(computer_science)#Tracing_garbage_collectors"">Garbage collectors wiki</a>
自从 Lisp 引入垃圾回收机制以来, 这个特性逐渐成为了高级语言的标配。 垃圾回收器的实现机制分为两种:
Tracing Garbage collectors
Reference counting
而由于 Reference counting 有一些明显的短板:
- 循环引用
为了避免这个问题, 需要复杂的算法来识别可以被回收的循环引用; 或者,让程序员显性说明某个引用是 weak 的, 不影响垃圾回收。
需要额外的存储空间(存放引用计数)
代码执行速度的略微影响(多了增加/减少引用计数的语句)
原子性 (在多线程环境中要保证引用计数的原子性)。
所以大部分的垃圾回收器都是使用 Tracing Garbage collectors 的。
特别的, ARC 并不算是垃圾回收器, 不过它依赖于 Reference counting 的原理,所以也会遇到这些问题。 就像一个懂事的小女孩,帮我们处理了很多繁琐的工作,虽然她有点小迷糊, 有时候需要我们明确告诉她要怎么做,但是还是很惹人喜爱。