前邊鋪墊了那麼多,現在終於要講到垃圾回收這個東西了。
弧
這不是方舟……這是自動引用計數(Automatic Reference Counting),這個東西是蘋果用來管理內存的。
它的功能就是那個垃圾堆上的垃圾回收器。它能夠保證所有在堆上運行的對像被釋放後不會一直駐留在堆上。保證了那塊內存會再分配給其他要使用的對像上。
何為釋放
說到對象會不會被垃圾回收器給收走,那就要看引用會不會釋放了:我們這樣來分辨一個對像是否還在被使用——當沒有引用再指向某個對象的時候,我們就說它已經被釋放了——因為它再也不會被訪問到了。
釋放的方法
我們一般有三種方法可以釋放掉對一個對象的引用:
- 把引用放在方法裡,方法出棧,引用完蛋——對像沒了引用,會被回收。
- 引用更改指向到其他對像上——原本的對像沒有了引用,會被回收。
- 直接清空引用——前提是你聲明的儲存器是可選類型。
好了,現在我們分別來舉幾個栗子看:
1 2 3 4 5 |
func run() { _ = SmallGun(ammo1: 8, isSafe2: true) } run() |
方法執行完畢出棧,臨時變量沒了——對象自然就釋放掉了。arc 發現對像沒有引用了,那麼這個對象就會被收回,留下的空間給別人用。
1 2 3 |
var gun1 = SmallGun(ammo1: 8, isSafe2: true) var gun2 = SmallGun(ammo1: 9, isSafe2: false) gun1 = gun2 |
我們吧 gun2 的引用賦給 gun1,那麼 gun1原來的引用就被覆蓋掉了,那對應的對像也就只能等著被回收啦!
1 2 3 |
var gun1:Gun? gun1 = SmallGun(ammo1: 8, isSafe2: true) gun1 = nil |
我們重新給 gun1 賦值為空,那麼引用沒了,對像也就隨之完蛋了。
交叉引用
有這麼一種情況,會導致自動引用計數捉雞,我們來看看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Aaa { var b:Gun? } var gun1:Gun? var aaa:Aaa? gun1 = SmallGun(ammo1: 8, isSafe2: true) aaa = Aaa() gun1?.ccc = aaa aaa?.b = gun1 aaa = nil gun1 = nil |
我們稍微修改了剛才的類,增加了一個 Aaa 類的引用屬性,然後創建了這個 Aaa 的類,這個類包含了一個 Gun 類的引用屬性,這時候我們把它兩個互相引用,然後再釋放掉……等等,真的可以釋放掉嗎?
然而並不能。
我們回顧一下一個對像被釋放的定義:當對像沒有引用就稱作被釋放——可是這兩個對像都還有互相之間的引用呢!
所以,我們雖然訪問不到這兩個對象了,但是它們兩個互相還引用著,arc 無計可施,你一樣也不能再訪問到它們——這就叫著名的“內存洩露”。
弱引用
這時候,我們要打破這個環路,讓引用能夠斷開,這樣才能讓這個互相引用的對像一個一個地被釋放掉。我們需要將其中一個聲明為弱引用,只有這樣才能讓 arc 斷開他們之間的環路。
1 2 3 |
class Aaa { weak var b:Gun? } |
這樣就好了,環路會被打斷,洩露的內存也被找回來了。
弱引用的要求
- 弱引用必須是變量,不能是常量,因為它得可以被修改。
- 弱引用必須是可選項,它得能被清空。
本文由 落格博客 原創撰寫:落格博客 » 生存週期:自動引用計數
轉載請保留出處和原文鏈接:https://www.logcg.com/archives/1135.html
註釋