Swift)ARC


SWIFTは、自動参照カウント(ARC)を使用してアプリケーションのメモリ使用を管理します.
リファレンス回数を自動的に管理するため、ほとんどの場合、開発者はメモリ管理に関心を持つ必要はなく、ARCが使用しないことを自分で決定したインスタンスメモリを無効にします.
ただし、場合によっては、ARCは、メモリを管理するためにコードの特定の部分間の関係に関する情報を必要とする.
参照回数はクラスタイプのインスタンスにのみ適用され、値タイプの構造体、列挙タイプなどには適用されません.

ARC挙動


クラスの新しいインスタンスを作成するたびに、ARCはインスタンス内の情報を格納するために適切なサイズのメモリを割り当てます.
このメモリには、インスタンスの情報に関連するストアド・プロシージャ値もあります.他のインスタンスが使用されなくなった場合、ARCはインスタンスが占めるメモリを解放し、他の用途に使用でき、使用可能なメモリ領域があることを確認します.
ただし、ARCがまだ使用中のインスタンスがメモリから解放され、インスタンスのpropertyに近い場合、アプリケーションはクラッシュします.
ARCは、使用中のインスタンスを破壊しないように、プロセス、定数、変数のインスタンスへの参照を追跡します.
したがって、ARCが少なくとも1つのインスタンスへの参照を持っている場合、インスタンスはメモリから破壊されません.

ARCの使用


次のコードは、ARCが実際にどのように動作しているかを確認するコードです.
// MARK: ARC

class Person {
    let name: String

    init(name: String) {
        self.name = name
        print("\(name) init")
    }

    deinit {
        print("\(name) deinit")
    }
}

var ref1: Person?
var ref2: Person?
var ref3: Person?

ref1 = Person(name: "스리니") // Person's ARC = 1
// Prints 스리니 init

ref2 = ref1 // Person's ARC = 2
ref3 = ref1 // Person's ARC = 3

ref1 = nil //  Person's ARC = 2
ref2 = nil //  Person's ARC = 1

ref3 = nil // Person's ARC = 0
// Prints 스리니 deinit

クラスインスタンス間の強い参照のループ


ARCはデフォルトで参照回数を追跡するため、インスタンスは使用されなくなり、インスタンスは自動的にメモリから解放されます.
ただし、絶対にメモリから解放してはいけない場合もあります.
たとえば、クラスのインスタンス間で強く相互参照されます.
これを강한 참조 순환といいます.
サンプルコードを次に示します.
class Person {
    let name: String
    var apart: Apartment?

    init(name: String) {
        self.name = name
        print("\(name) init")
    }

    deinit {
        print("\(name) deinit")
    }
}

class Apartment {
    let unit: String
    var tenant: Person?

    init(unit: String) {
        self.unit = unit
        print("\(unit) init")
    }

    deinit {
        print("\(unit) deinit")
    }
}

var frog: Person?
var helioCity: Apartment?

frog = Person(name: "개굴")
helioCity = Apartment(unit: "송파")
変数frogはPersonを強く参照し、helioCityはApartmentを強く参照します.
frog?.apart = helioCity
helioCity?.tenant = frog
PersonインスタンスのparateはHelioCityを参照し,PartmentのテナントはPersonを参照し,相互参照数は2である.
frog = nil
helioCity = nil
各変数をnilに割り当てて参照を解除します.ただし、インスタンスは解除されません.
図に示すように、強いループリファレンスが発生し、メモリが漏洩します.

クラスインスタンス間の強い参照ループの問題の解決


強いループ参照を解決するために、弱い参照を使用する方法と、非プライマリ参照を使用する方法があります.
どちらのリファレンスも、ARCでreferencecountを追加するのではなく、インスタンスを参照するため、ループの問題を解決できます.
したがって、上記の例では、
weak var tenant: Person?
変換すると、画像は以下のようになり、ループは解除されます.


「微笑み」を参照


弱いリファレンスとは異なり、未知のリファレンスは現在のリファレンスと同じライフサイクルまたはそれ以上のライフサイクルを持つため、常にこの値を持つことが望ましい.
したがって,非固有参照はNilを絶対に割り当てない.
微小油参照は参照対象インスタンスが常に存在すると考えられるため、インスタンスが無効になっている場合、それに近づくと運転時エラーが発生します.
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") }
}

エンクロージャでの強参照のループ


強参照サイクルは、エンクロージャに関連する可能性があります.
Closerでselfが切り取られるからです.
この問題を解決するために,モジュールキャプチャリストを用いた.
次のクラスでは、モジュールでselfを選択できます.文字と自己.スクリーンショットname.
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")
    }
}
asHTMLのモジュールは、以下のように他のモジュールに変更することもできます.
let heading = HTMLElement(name: "h1")
let defaultText = "some default text"
heading.asHTML = {
    return "<\(heading.name)>\(heading.text ?? defaultText)</\(heading.name)>"
}
print(heading.asHTML())
// Prints "<h1>some default text</h1>"
asHTMLはlazy propertyとして宣言されています.HTMLはタグとテキストの準備ができたら使用しなければならないからです.
このコードを実行すると、次の結果が得られます.
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// Prints "<p>hello, world</p>"
これにより、強力なループ参照に陥ります.

エンクロージャ内の強い参照サイクルの問題を解決


エンクロージャ内の強い参照ループの問題を解決するには、強い参照ではなく、キャプチャ参照に弱い参照または弱い参照を指定します.
弱いログか非弱いログかは、コード内で相互関係に依存します.
Closerではselfを明確にする必要があります.

取得リストの定義

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
}

弱い参照と小さな参照


先に参照を解除すると、約半日になります.
同じ時点または以降で無効にする場合に使用する微小油参照
スクリーンショットリストがnilにならない場合は、プライマリなしでなければなりません.
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")
    }
}