跟OC
一樣缺谴,swift
也是采取基于引用計數(shù)的ARC
內存管理方案(針對堆空間)
swift
中的ARC
有3
種引用:
- 強引用:默認都是強引用
- 弱引用:通過
weak
定義弱引用
1. 必須是可選類型的var
朝扼,因為實例銷毀后城榛,ARC
會自動將弱引用設置為nil
2.ARC
自動給弱引用設置nil
時疮装,不會觸發(fā)屬性觀察器 - 無主引用:通過
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協(xié)議遵守AnyObject汰翠,所以它只能被類遵守
unowned var p10: Person?
unowned var p11: AnyObject?
unowned var p12: Livable?
Autoreleasepool
自動釋放池
在需要緩解內存壓力的地方,使用autoreleasepool
autoreleasepool {
print("kkkk")
}
循環(huán)引用
-
weak
昭雌、unowned
都能解決循環(huán)引用的問題复唤,unowned
要比weak
少一些性能消耗
1. 在生命周期中可能會變成nil
的使用weak
2. 初始化賦值之后再也不會變成nil
的,建議使用unowned
閉包的循環(huán)引用
- 閉包表達式默認會對用到的外層對象產生額外的強引用(對外層對象進行了
retain
操作) - 下面代碼會產生循環(huán)引用城豁,導致
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
操作,導致計數(shù)器+1
,最終release
之后雳旅,計數(shù)器還是1
跟磨,Person
對象沒有釋放:
嘗試注釋掉
p.fn = { p.run() }
這行調用,再看下斷點匯編:這時候計算器已經是0
了攒盈,Person
對象被銷毀因為閉包表達式用到了外面的對象抵拘,產生了強引用,導致無法釋放對象型豁。
- 在閉包表達式的捕獲列表聲明
weak
或者unowned
引用僵蛛,解決循環(huán)引用問題
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
屬性是閉包調用的結果迎变,那么不用考慮循環(huán)引用問題(因為閉包調用后充尉,閉包的生命周期就結束了)
class Person {
var age: Int = 0
lazy var getAge: Int = {
self.age
}()
deinit { print("Person deinit") }
}