【发布时间】:2017-01-15 15:19:40
【问题描述】:
是否正在进行某种程序分析以在运行时减少/删除引用计数?或者它们只是“自动”的,因为程序员不需要手动增加和减少计数,但每当引用产生/消失时就会发生?
基本上,ARC 与普通引用计数有何不同?是否有任何已发表的论文讨论正在发生的事情,尤其是在使用程序分析的情况下?
【问题讨论】:
标签: swift memory-management automatic-ref-counting reference-counting
是否正在进行某种程序分析以在运行时减少/删除引用计数?或者它们只是“自动”的,因为程序员不需要手动增加和减少计数,但每当引用产生/消失时就会发生?
基本上,ARC 与普通引用计数有何不同?是否有任何已发表的论文讨论正在发生的事情,尤其是在使用程序分析的情况下?
【问题讨论】:
标签: swift memory-management automatic-ref-counting reference-counting
是否正在进行某种程序分析以在运行时减少/删除引用计数?
不,正在进行程序分析以自动插入维护引用计数的调用。
或者它们只是“自动”的,因为程序员不需要手动增加和减少计数......
没错。在 ARC 之前,Objective-C 程序员必须小心地在想要维护对对象的引用时调用 -retain,并在完成引用时调用 -release。这些方法递增和递减对象的引用计数。使用 ARC,编译器会找出应该在哪里添加这些调用并插入它们(或等效代码)。所以从程序员的角度来看,引用计数是自动的。
【讨论】:
自动引用计数 (ARC) 涉及编译时的静态分析,但不执行任何运行时分析。
将 ARC 概念化的简单方法是两遍过程。首先,它会检查您的代码并在引入对象引用和/或超出范围的地方插入内存管理代码(语义上等同于 ARC ObjC 之前的 retain 和 release 调用)。所以这个:
func doSomething() -> Object {
let thing1 = Object(name: "foo")
var thing2 = Object(name: "bar")
thing2 = Object(name: "baz")
return thing2
}
...变成(以某种编译器内部的中间形式)如下:
func doSomething() -> Object {
let thing1 = Object(name: "foo")
__retain(thing1) // increment reference count so thing1 sticks around
var thing2 = Object(name: "bar")
__retain(thing2) // increment reference count so thing2 sticks around
let oldThing2 = thing2 // thing2 gets replaced on the next line
thing2 = Object(name: "baz")
__release(oldThing2) // get rid of the thing that got overwritten
__retain(thing2) // hold onto the new thing that replaced it
__release(thing1) // end of scope, so nothing else will use thing1
return __autorelease(thing2) // relinquish ownership of thing2,
// but only upon handing ownership over to our caller
}
引入对象引用的时间以及它们超出范围或移交给调用者的时间(通过return 语句)是逐行满足程序语义的最低要求,但到此为止分析级别导致冗余内存管理。 (例如,请注意 thing1 在创建后从未使用过。)因此 ARC 所做的下一件事是进行流分析以消除冗余并执行其他优化,使我们的示例伪代码更像这样:
func doSomething() -> Object {
_ = Object(name: "foo") // formerly thing1
// Object.init(name:) is called in case it has side effects,
// but the result is immediately discarded because it's not used
_ = Object(name: "bar") // formerly thing2
// again we call the initializer but discard the result,
// because the original thing2 is immediately overwritten
let thing2 = Object(name: "baz")
// no retain, because nothing happens between here and return
// that could affect the memory lifetime of thing2
return __magic_autorelease(thing2)
// and there's other voodoo we can do to ensure handoff to the caller
// without getting into retain count tracking or autorelease pools
}
这显然是一个非常随意的概述 - 引擎盖下还有很多事情要做,所以真正的实现并不完全适合这个简单的两步过程,但在概念上它非常相似。有关更详细的讨论,请查看:
前两个是关于 Objective-C 中的 ARC,但 Swift 中的 ARC 密切相关,所以如果你想知道 ARC 的真正工作原理,最好看看它的 ObjC 起源。
【讨论】: