前の例で、ARC は生成した新しい Person インスタンスに対する参照数を追跡し、Person インスタンスが必要なくなった時に割り当て解除することができました。

けれども、クラスインスタンスの強い参照が決してゼロになるようなことがないようにコードを記述することも可能です。2 つのクラスインスタンスが他方を生存させ続けるような、互いに強い参照を持つ場合に起こります。これを強い参照の循環と呼びます。

強い参照の代わりに弱い参照または非所有の参照としてクラス間の関係を定義することで、強い参照の循環を解決します。この処理は、Resolving Strong Reference Cycles Between Class Instancesで説明されています。しかしながら、強い参照の循環を解決する方法について学習する前に、そのような循環が起こる原因について理解するのは役に立ちます。

次は、どのように強い参照の循環が偶然に生成されるのかの例です。この例では、アパートのブロックとその居住者をモデル化する 2 つのクラス Person と Apartment を定義しています。

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") }
}

すべての Person インスタンスが String 型の name プロパティと、初めは nil のオプショナル apartment プロパティを持ちます。常にアパートがあるわけではないため、apartment プロパティをオプショナルにしています。

同じように、すべての Apartment インスタンスが String 型の unit プロパティと、初めは nilのオプショナル tenant プロパティを持ちます。常にテナントがいるわけではないため、tenant プロパティをオプショナルにしています。

また、両クラスともにクラスのインスタンスが、デイニシャライズされたことを出力するデイニシャライザを定義しています。Person と Apartment のインスタンスが、期待通りに割り当て解除されたかどうかを確認できるようになります。

次のコードスニペットは、その次に特定の Apartment と Person インスタンスに設定される、オプショナル型の変数 john と unit4A を定義しています。両変数ともに、オプショナルであるため初期値は nil になります。

var john: Person?
var unit4A: Apartment?

特定の Person インスタンスと Apartment インスタンスを生成して、これらの新しいインスタンスを変数 john と unit4A に代入することができます。

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

次は、2 つのインスタンスを生成して代入した後に、強い参照がどのようになっているかを示しています。変数 john は新しい Person インスタンスに対する強い参照を持ち、変数 unit4A は新しい Apartment インスタンスに対する強い参照を持ちます。

image: referenceCycle01_2x

次に、パーソンがアパートを持ち、アパートがテナントを持つように 2 つのインスタンスをつなぎます。インスタンスのプロパティに設定することができるよう、オプショナル変数の john と unit4Aで保管されたインスタンスをアンラップしてアクセスするために、エクスクラメーションマーク (!) が使われていることに注目してください。

john!.apartment = unit4A
unit4A!.tenant = john

image: referenceCycle02_2x

あいにく、2 つのインスタンスをつなげたことで、強い参照の循環ができてしまいます。Person インスタンスは Apartment インスタンスに対する強い参照を持ち、Apartment インスタンスは Person インスタンスに対する強い参照を持ちます。従って、変数 john と unit4A で保持された強い参照を切っても、参照カウントはゼロにならず、インスタンスは ARC によって割り当て解除されません。

john = nil
unit4A = nil

2 つの変数に nil を設定した場合でも、どちらのデイニシャライザも呼び出されないことに注目してください。強い参照の循環が、Person と Apartment のインスタンスが割り当て解除することを妨げ、アプリにメモリリークを引き起こします。

次は、変数 john と unit4A に nil を設定した後に、強い参照がどのようになっているかを示しています。

image: referenceCycle03_2x

Person インスタンスと Apartment インスタンス間に強い参照が残ったまま、切れなくなっています。


Portions of this page are translations based on work created and shared by Apple and used according to terms described in the Creative Commons Attribution 4.0 International License.