在Swift中,ARC(自动引用计数)的概念在
官方文档中已经很清楚地解释了。下面是4个重要方面的简要总结:
1. 基础知识
ARC将每个类实例与一个retainCount整数关联起来。这个值表示对该特定实例的强引用数量。当这个数字变为0时,该实例使用的内存被释放。
class Something {}
var aVariable = Something()
// now the reference counting for this particular instance of Something is 1
现在,由于其
retainCount
值为
1
,我们的
Something
实例被保留在内存中。
var anotherVariable = aVariable
现在我们有
2
个强引用指向我们的
Something
实例。很好!它的
retainCount
现在是
2
,实例仍然保留在内存中!
aVariable = nil
retainCount
刚刚变成了
1
。没问题,实例仍然在内存中。
anotherVariable = nil
最终我们的实例的retainCount
变为了0
。这意味着该实例已被释放,不再可访问。
2. 当你使用完变量后,是否应该将其设置为nil?
不需要。 实际上,当一个变量超出范围时,ARC会自动减少所引用实例的retainCount
。
在下面的示例中,在最后一个}
之前,Something
实例的retainCount
会减少并达到值0
(并被释放)。
func doSomethingUseful() {
let something = Something()
}
在一般情况下,您不需要将变量设置为
nil
以强制ARC释放引用的实例。
另一个例子:
class Son {}
class Father {
var sons: [Son]
init (sons: [Son]) {
self.sons = sons
}
}
func lifeGoesOn() {
let son = Son()
let father = Father(sons: [son])
}
你可以把这个看作多米诺骨牌效应。当一个实例被释放时,它对其他实例的所有引用都会被删除。因此,被引用实例的保留计数将减少。如果变为0,它们就会被释放。依此类推...
3. 保留循环
好的,如果我有两个如下类会发生什么?
class Son {
let father:Father
init(father:Father) {
self.father = father
}
}
class Father {
var sons: [Son]
init (sons: [Son]) {
self.sons = sons
}
}
现在让我们创建一个从父亲到儿子的引用,以及从儿子到父亲的相反引用。请保留
father
和
son
标签,不要进行解释。
func lifeGoesOn() {
var father = Father(sons:[])
var son = Son(father: father)
father.sons.append(son)
}
在函数结束时,父变量超出作用域,因此
Father
实例的
retainCount
变为
1
。类似地,变量
son
超出作用域,
Son
实例的
retainCount
也变成了
1
。
问题在于,
Son
实例引用了
Father
实例(使该实例处于存活状态)。而
Father
实例引用了
Son
实例。这两个实例不应该再存在于内存中了。由于所有引用它们的变量都已经消失,程序员无法访问它们。
这是一个问题。
4. 弱引用
在编写代码时,应注意强引用循环。接下来我们来看如何重构代码以解决这个问题。
class Son {
weak var father:Father?
init(father:Father) {
self.father = father
}
}
现在,从一个`Son`到其`Father`的引用是`weak`。这意味着当ARC计算实例的(强)引用数时,它不会计数。这解决了前一段中出现的问题。
我希望现在主题已经更加清晰了。还有几种情况我没有涉及到。再次强调,官方文档非常好,详尽。
更新(为了更好地回答下面的评论)
如果您有一个自定义的`UIViewController`(称之为`MyCustomViewController`),并且该类具有对对象的强引用,请看看会发生什么。
class MyCustomViewController : UIViewController {
var something = Something()
}
class Something {
init() {
debugPrintln("The instance of Something has been created")
}
deinit {
debugPrintln("The instance of Something has been freed")
}
}
当您提供一个
MyCustomViewController
时,会创建一个
MyCustomViewController
实例。然后还会创建一个
Something
实例。
现在,
UINavigationController
引用
MyCustomViewController
实例,因此
retainCount=1
。
类似地,
Something
实例由
MyCustomViewController
实例引用,因此它的
retainCount=1
。
所以,
UINavigationController
实例“保持”
MyCustomViewController
实例存在。而
MyCustomViewController
实例又“保持”
Something
实例存在。
接下来,您决定解除
MyCustomViewController
。因此,iOS动画使其离开屏幕。当它不再可见时,从
UINavigationController
实例到
MyCustomViewController
实例的引用将被删除。
这意味着
MyCustomViewController
实例的
retainCount
变为0,因为:现在没有人引用它!
因此,
MyCustomViewController
实例将从内存中删除。在此过程中,它的属性被设置为null。
现在,
Something
实例的
retainCount
已变为0。因此,它也将从内存中删除。
最后,我重写了
Something
的
init
和
deinit
方法,以便您可以跟踪相关实例的分配和释放。查看日志(或使用断点),您可以验证我在这里所说的。
希望这有所帮助。
nil
,因为当视图控制器和视图被释放时,这将自动发生。请参见https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html。 - Aaron Brager