跟OC
一样,swift
也是采取基于引用计数的ARC
内存管理方案(针对堆空间)
swift
中的ARC
有3
种引用:
- 强引用:默认都是强引用
- 弱引用:通过
weak
定义弱引用
1. 必须是可选类型的var
,因为实例销毁后,ARC
会自动将弱引用设置为nil
2.ARC
自动给弱引用设置nil
时,不会触发属性观察器 - 无主引用:通过
unowned
定义无主引用
1. 不会产生强引用,非可选类型,实例销毁后仍然储存着实例的内存地址(类似OC
中的unsafe_unretained
)
2. 试图在实例销毁后访问无主引用,会产生运行时错误(野指针)
weak
、unowned
的使用限制
-
weak
、unowned
只能用在类实例上面
protocol Livable: AnyObject { }
class Person { }
weak var p0: Person?
weak var p1: AnyObject?
weak var p2: Livable? //因为Livable协议遵守AnyObject,所以它只能被类遵守
unowned var p10: Person?
unowned var p11: AnyObject?
unowned var p12: Livable?
Autoreleasepool
自动释放池
在需要缓解内存压力的地方,使用autoreleasepool
autoreleasepool {
print("kkkk")
}
循环引用
-
weak
、unowned
都能解决循环引用的问题,unowned
要比weak
少一些性能消耗
1. 在生命周期中可能会变成nil
的使用weak
2. 初始化赋值之后再也不会变成nil
的,建议使用unowned
闭包的循环引用
- 闭包表达式默认会对用到的外层对象产生额外的强引用(对外层对象进行了
retain
操作) - 下面代码会产生循环引用,导致
Person
对象无法释放(deinit
无调用)
class Person {
var fn: (() -> ())?
func run() { print("Person run") }
deinit { print("Person deinit") }
}
func test() {
let p = Person()
p.fn = { p.run() }
}
test()
通过汇编可以看到,进行了retain
操作,导致计数器+1
,最终release
之后,计数器还是1
,Person
对象没有释放:
尝试注释掉
p.fn = { p.run() }
这行调用,再看下断点汇编:这时候计算器已经是0
了,Person
对象被销毁因为闭包表达式用到了外面的对象,产生了强引用,导致无法释放对象。
- 在闭包表达式的捕获列表声明
weak
或者unowned
引用,解决循环引用问题
func test() {
let p = Person()
p.fn = {
[weak p] in
p?.run()
}
}
test()
p.fn = {
[unowned p] in
p.run()
}
甚至可以自定义名称:
p.fn = {
[weak wp = p, unowned up = p, a = 10 + 20] in
wp?.run()
}
- 如果想在定义闭包属性的同时引用
self
,这个闭包必须是lazy
的(因为在实例初始化完毕之后才能引用self
)
class Person {
lazy var fn: (() -> ()) = {
[weak self] in
self?.run()
}
func run() { print("Person run") }
deinit { print("Person deinit") }
}
func test() {
var p = Person()
p.fn()
}
test()
- 如果
lazy
属性是闭包调用的结果,那么不用考虑循环引用问题(因为闭包调用后,闭包的生命周期就结束了)
class Person {
var age: Int = 0
lazy var getAge: Int = {
self.age
}()
deinit { print("Person deinit") }
}
网友评论