Automatic Reference Counting-Swift

godiscoder發表於2016-12-21

Automatic Reference Counting

迴圈引用(strong reference cycles)

1、程式碼示例:

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

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

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john
//雖然你置為nil,但是這兩個類的例項並沒有釋放記憶體,沒有執行print語句
john = nil
unit4A = nil複製程式碼

2、兩種解決辦法:

  • 當其他的例項有更短的生命週期的時候,使用weak
  • 當其他的例項有相同的或者更長的生命週期的時候,使用unowened

3、weak和unowened的使用

  • weak的使用

將上述程式碼的Apartment中的Person例項前新增weak關鍵字,在執行上述程式碼就會執行print

weak var tenant: Person?複製程式碼
  • unowened的使用
class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) {
        self.name = name
    }
    deinit { print("\(name) is being deinitialized") }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card #\(number) is being deinitialized") }
}

var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil
// Prints "John Appleseed is being deinitialized"
// Prints "Card #1234567890123456 is being deinitialized"複製程式碼

閉包的迴圈引用(strong references for closures)

class HTMLElement {

    let name: String
    let text: String?

    lazy var asHTML: () -> String = {

        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) is being deinitialized")
    }

}

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil//不會執行print複製程式碼

可以定義capture list來解決

//有引數
lazy var someClosure: (Int, String) -> String = {
    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
    // closure body goes here
}
//無引數
lazy var someClosure: () -> String = {
    [unowned self, weak delegate = self.delegate!] in
    // closure body goes here
}複製程式碼

修改上述例子後的程式碼:

class HTMLElement {

    let name: String
    let text: String?

    lazy var asHTML: () -> String = {
        [unowned self] in
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) is being deinitialized")
    }

}

var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil//執行print複製程式碼

Tip:

  • ARC只是用於類,不適用於結構體和列舉
  • 當ARC設定的weak引用指為nil時,屬性觀察者不會被呼叫
  • 當你在確定引用的例項永遠不會銷燬的時候,使用unowened,如果你想訪問被釋放的owened屬性,你會遇到執行時錯誤。
  • owened有安全和不安全兩種模式,上面示例中為安全模式,你可以使用unowned(unsafe)來實現不安全模式。如果你試圖訪問被釋放的不安全指向的例項,你的程式將會訪問該例項曾經儲存的記憶體地址,這是一個不安全的操作

github

相關文章