Swift裏你可能不知道的事兒(1)-Reference cycle引發的內存泄漏

Swift裏你可能不知道的事兒(1)

泊學視頻
泊閱文檔swift

Reference cycle引發的內存泄漏

縱然Swift使用ARC(Automatic Reference Counting)爲咱們打理內存,這並不表明它面對任何狀況都足夠聰明。尤爲是當對象之間存在相互引用的時候,更是容易因爲reference cycle致使內存沒法釋放。固然,這並不是咱們本意,只是有時這樣的問題發生的不甚明顯。Swift爲咱們提供了一系列語言機制來處理reference cycle,而咱們也應該時刻保持警醒,避免內存泄漏。spa

*「ARC只針對類對象才生效,struct和enum都是值類型,它們的對象並不被ARC管理。」
特別提示*code

ARC是如何工做的?

Swift使用「引用計數(reference count)」來管理類對象的生命週期,避免類對象在「仍被使用」的時候被意外釋放。爲了觀察reference count、對象構建和對象釋放之間的關係,咱們先來定義一個類:視頻

class Person {
    let name: String
    
    init(name: String) {
        self.name = name
        print("\(name) is being initialized.")
    }
    
    deinit {
        print("\(name) is being deinitialized.")
    }
}

接下來,咱們觀察下面代碼的的執行過程:對象

var ref1: Person?
var ref2: Person?

// Mars is being initialized.
// count = 1
ref1 = Person(name: "Mars")

// count = 2
ref2 = ref1

// count = 1
ref2 = nil

// count = 0
// Mars is being deinitialized.
ref1 = nil
  • Person構造時,init被調用,只有一個變量引用了這個對象,此時的reference count爲1;生命週期

  • 當咱們把ref1賦值給ref2時,它們指向相同的對象,此時的reference count爲2;內存

  • 當ref2爲nil時,此時的reference count恢復爲1;文檔

  • 當ref1爲nil時,reference count爲0,Tennat的deinit被調用,對象被釋放;get

在上面的例子裏,因爲ref1和ref2都會致使Person的reference count加1,所以它們叫作 strong reference 。這是Swift中的默認行爲。it

*「和帶有Garbage Collection的語言不一樣,當一個對象的reference count爲0時,Swift會當即刪除該對象。」
特別提示*

理解Reference cycle是如何發生的

對於Person這樣的單個對象,ARC能夠很好的默默爲咱們工做。可是, 當不一樣類對象之間存在相互引用時 ,指向彼此的strong reference就會致使reference cycle。ARC沒法釋放它們之中的任何一個。來看一個例子,咱們給Person添加一個Apartment:

class Person {
    let name: String
    var apartment: Apartment?
    
    init(name: String) {
        self.name = name
        print("\(name) is being initialized.")
    }
    
    deinit {
        print("\(name) is being deinitialized.")
    }
}

class Apartment {
    let unit: String
    var tenant: Person?
    
    init(unit: String) {
        self.unit = unit
        print("Apartment \(unit) is being initialized.")
    }
    
    deinit {
        print("Apartment \(unit) is being deinitialized.")
    }
}

因爲Person不必定會租Apartment,Apartment也不必定有房客,所以,Person.apartment和Apartment.tenant都是一個Optional,它們能夠爲nil。

接下來,咱們分別建立一個Person和Apartment對象,而後把變量設置爲nil,就能夠看到Person和Apartment被構建和銷燬了。

// Mars is being initialized
// count = 1
var mars: Person? = Person(name: "Mars")
// Apartment 11 is being initialized
// count = 1
var apt11: Apartment? = Apartment(unit: "11")

// count = 0
// Mars is being deinitialized
mars = nil
// count = 0
// apartment is being deinitialized
apt11 = nil

bo-reading-sep-person-apt11@2x.jpg
接下來,若是咱們讓mars和apartment分別指向彼此:

// ... Create mars and apartment

// mars.count = 2
mars!.apartment = apartment
// apartment.count = 2
apt11!.tenant = mars

// ... Set mars and apartment to nil

這時,儘管咱們把mars和apt11設置爲nil,Person和Apartmetn的deinit也不會被調用了。由於它們的兩個member(apartment和tenant)是一個strong reference,指向了彼此,讓對象仍舊「存活」在內存裏。可是,mars和apt11已經被設置成nil,咱們也已經無能爲力了。這就是類對象之間的reference cycle。

接下來?

在理解了ARC的基本工做原理以及reference cycle的成因以後,咱們將在接下來的一些列視頻裏,瞭解如何經過weak和unowned reference來解決reference cycle。

相關文章
相關標籤/搜索