Swift内存分析
Swift使用ARC管理对象的的内存,ARC只适用于引用类型,引用计数会带来一些副作用,比如循环引用,所以Swift给我们提供了一些引用修饰来适应不同的情景,weak
,strong
,unowned
,隐式可选
引用计数为0销毁对象,不为0不销毁
class A {
deinit {
print("析构器被调用")
}
}
var a:A? = A()
var b:A? = a
var c:A? = b
a = nil
b = nil
c = nil//销毁
虽然Swift使用了ARC来帮助程序员管理内存,但是仍有一些特殊情况,需要程序员自己处理(添加各种标记帮助ARC工作)
循环引用
自己引用自己
class A {
var a:A?
deinit {
print("A析构器被调用")
}
}
var a:A? = A()
a?.a = a
a = nil
相互引用
class A {
var b:B?
deinit {
print("A析构器被调用")
}
}
class B {
var a:A?
deinit {
print("B析构器被调用")
}
}
var a:A? = A()
var b:B? = B()
a?.b = b
b?.a = a
a = nil
b = nil
如何解决?
弱引用和无主引用
弱引用用weak
标记,表示引用而不持有,引用计数不变,默认所有引用都是strong
(可省略)
class A {
weak var a:A?//变成弱引用
deinit {
print("A析构器被调用")
}
}
var a:A? = A()
a?.a = a
a = nil
class A {
var b:B?
deinit {
print("A析构器被调用")
}
}
class B {
weak var a:A?//将其中一个写成弱引用
deinit {
print("B析构器被调用")
}
}
var a:A? = A()
var b:B? = B()
a?.b = b
b?.a = a
a = nil
b = nil
weak
引用的位置决定了对象的释放顺序
weak
类型的引用在实例被销毁后自动置为nil
(不会触发属性观察器), weak
引用必须是 var
可选,因为weak
会在对象销毁时被置为nil
无主引用
无主引用和弱引用类似,都不会对实例进行强引用。区别是,无主引用的实例被销毁后并不会将无主引用置为nil。使用场景就是,必须保证无主引用始终指向一个未销毁的实例,无主引用必须是非可选的变量。使用关键字unowned
标记无主引用
class A {
var b:B?
deinit {
print("A析构器被调用")
}
}
class B {
unowned let a:A
init(a:A) {
self.a = a
}
deinit {
print("B析构器被调用")
}
}
var block = {
var a = A()
var b = B(a: a)
a.b = b
}
block()
无主引用以及隐式解析可选属性
两个实例都可以为nil
,使用weak
打破循环强引用
两个实例其中一个可以为nil
,另一个必须有值,使用unowned
然而还存在第三种情况,两个都不能为nil
,这种情况,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性(隐式可选解析存在的意义😂)。
class Country {
let name: String
var capitalCity: City!
init(name: String, capitalName: String) {
self.name = name//因为capitalCity是可选类型,所以此时已经初始化完毕,此后可以引用`self`
self.capitalCity = City(name: capitalName, country: self)
}
}
class City {
let name: String
unowned let country: Country
init(name: String, country: Country) {
self.name = name
self.country = country
}
}
这块理解的还不是很到位,就用官方的例子了
闭包引起的循环强引用
闭包是引用类型,一个类中有一个闭包属性,闭包属性中又使用强 self
,就会造成循环强引用
class A {
var a:Int = 2
var clousre = {
}
func abc() {
weak var weakSelf = self;
self.clousre = {
weakSelf?.a = 4
}
}
deinit {
print("A析构器被调用")
}
}
var block = {
var a = A()
a.abc()
}
block()
可以将self
转为weak
,在闭包中使用类的成员必须使用self
来访问,提醒程序员注意闭包的捕获特性,造成循环引用
另外一种方法是使用捕获列表
class A {
var a:Int = 2
var clousre = {
}
func abc() {
self.clousre = {
[weak self] in
self?.a = 4 //加了weak就变成可选的了,因为weak修饰的可能会被置为nil
}
}
deinit {
print("A析构器被调用")
}
}
var block = {
var a = A()
a.abc()
}
block()
在闭包参数前面定义[weak self]
,修饰符 和 引用名,其实还是转换成弱引用和无主引用,修饰符如何选择?还是根据上述三种场景
1、两个实例相互引用,同时销毁,用unowned
2、捕获的引用可以是nil
时,用weak