前の例で、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
インスタンスに対する強い参照を持ちます。
次に、パーソンがアパートを持ち、アパートがテナントを持つように 2 つのインスタンスをつなぎます。インスタンスのプロパティに設定することができるよう、オプショナル変数の john
と unit4A
で保管されたインスタンスをアンラップしてアクセスするために、エクスクラメーションマーク (!
) が使われていることに注目してください。
john!.apartment = unit4A
unit4A!.tenant = john
あいにく、2 つのインスタンスをつなげたことで、強い参照の循環ができてしまいます。Person
インスタンスは Apartment
インスタンスに対する強い参照を持ち、Apartment
インスタンスは Person
インスタンスに対する強い参照を持ちます。従って、変数 john
と unit4A
で保持された強い参照を切っても、参照カウントはゼロにならず、インスタンスは ARC によって割り当て解除されません。
john = nil
unit4A = nil
2 つの変数に nil
を設定した場合でも、どちらのデイニシャライザも呼び出されないことに注目してください。強い参照の循環が、Person
と Apartment
のインスタンスが割り当て解除することを妨げ、アプリにメモリリークを引き起こします。
次は、変数 john
と unit4A
に nil
を設定した後に、強い参照がどのようになっているかを示しています。
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.