析构过程
析构过程是构造过程的逆向过程,当一个类的实例被释放的时候,在内存中消失前,析构器会被调用。
swift依然通过ARC管理内存。ARC相关内容,在后面会说。
原型
deinit{
//清理工作在这里
}
由于swift是ARC管理内存,所以析构过程已经不像c++那么重要了。
class MyClass {
var myNum = 0
init() {
myNum = 1
}
deinit {
myNum = 0
println(“myNum \(myNum)")
}
}
var myInstance:MyClass? = MyClass()
println(myInstance!.myNum)
myInstance = nil //deinit在此时被自动调用
ARC
ARC机制会跟踪每一个实例正在被多少属性,常量以及变量所引用,只要这个实例的引用数不为0,那么这个实例就不会被销毁。那么它的deinit也不会被调用。
文档中,这一章,只讲了两件事儿:
1. 实例之间的循环引用是怎么发生的。
2. 如何消除循环引用导致的内存泄露。
文档用了满多的篇幅写了这两件事,足以说明,ARC并不像想像中那么美好,虽然不像C/C++中那样,要求程序员用一些精力来管理内存,但ARC也并没有把这种精力降低了多少,反而,一切看似美好的东西,导致的难查的BUG会更难查。对底层机制隐藏的太多,导致程序员的底层思维得不到锻炼的结果吧。
这一章,没什么太多想说的,管方的图解已经比文字表达的要清晰得多了,所以只是留些图,便于查阅。
下面的图解,基于这两个类。这里可以看到,两个类里都分别有另一个类的可选类型的变量。那么,结果就是导致了他们互相持有对方的handle:
懂OO的都知道,这种情况是强关联。
在ARC中,当把john 和 number73都赋值为nil的时候,虽然看似断掉了john和number73这两个引用了,但是,在实例中,还有apartment 与 tenant两个引用没有断掉,于是,我们会想到,把这两个写到deinit中去,让他们断开引用关系:
在Person类中的deinit中加入:
apartment = nil
在Apartment类中的deinit中加入:
tenant = nil
但是,这样依然不行,因为在前面我们说过,deinit是在实例被释放的时候发生的,而实例的释放又是在ARC管理中的实例引用计数为0的时候发生的。所以,deinit并不会被调用。这样就导致了内存泄漏。所以,无论如何,都不可以出现两个实例互相强引用的情况,必然导致内存泄漏。
弱引用
为了解决上面的循环强引用,引入一个叫做“弱引用”的方式,关键字是weak:
这样的话,weak引用,不会阻止ARC回收实例,也就是说,一个实例,如果没有被强引用,哪怕有成百上千个弱引用持有它的话,它依然可以被释放。(weak可能会导致引用计数不增加?我不知道答案)
无主引用(Unowned References)
与刚刚说的弱引用,作用是一样,但弱引用是给可选型用的,而无主引用是给非可选型用的。
闭包导致的循环强引用
当HTMLElement类的实例在初始化的时候,闭包作为asHTML属性的默认值,与HTMLElement的实例之间建立了强引用关系,也就是我们开始提到无论如何都不要发生的事情。。。。
我们需要让闭包变成无主引用(前面说过,弱引用是给可选型用的,而这里闭包明显不是一个可选型):
在闭包中,使用 [unowned self] in
总结
从上面的例子可以看得出来,在引用关系中,我们可以分得出上下级关系(类的实例为最上级),所有的下级引用上级的情况,要么定义为弱引用(可选型),要么定义为无主引用,就可以避免循环引用而导致的内存泄漏。