name
という名前の定数格納プロパティを定義する Person
というシンプルなクラスから始まります。Person
クラスには、インスタンスの name
プロパティを設定し、初期化が進行中だということを示すメッセージを出力するイニシャライザがあります。インスタンスの割り当てが解除されたときにメッセージを出力するデイニシャライザもあります。Person?
型の 3 つの変数を定義しています。これらの変数は、後で出てくるコードスニペットで新しい Person
インスタンスへの複数の参照を設定するために使用されます。これらの変数はオプショナルの型(Person?
、Person
ではない)のため、nil
で自動的に初期化され、現在 Person
インスタンスを参照していません。Person
インスタンスを作成して、次の 3 つの変数のいずれかに割り当てることができます。Person
クラスのイニシャライザを呼び出した時点で、"John Appleseed is being initialized"
というメッセージが出力されることに注目してください。これは、初期化が行われたことを確認します。Person
インスタンスが reference1
変数に割り当てられているため、reference1
から新しい Person
インスタンスへの強参照があります。少なくとも 1 つの強参照があるため、ARC はこの Person
をメモリに保持し続け、割り当てが解除されないようにします。Person
インスタンスをさらに 2 つの変数に割り当てると、そのインスタンスへのさらに 2 つの強参照ができます。Person
インスタンスへ 3 つの強参照があります。nil
を代入してこれらの 2 つの強参照(元の参照を含む)を解除すると、1 つの強参照が残り、Person
インスタンスの割り当てが解除されません。Person
インスタンスの割り当てを解除しません。強参照がなくなった時点で、Person
インスタンスを使用していないことが明らかになります。Person
インスタンスへの参照の数を追跡し、不要になったらその Person
インスタンスの割り当てを解除できます。Person
と Apartment
という 2 つのクラスを定義しており、アパートのブロックとその住人をモデル化しています。Person
インスタンスには、String
型の name
プロパティと、最初は nil
のオプショナルの apartment
プロパティがあります。人は常にアパートを所有しているとは限らないため、apartment
のプロパティはオプショナルです。Apartment
インスタンスには String
型の unit
プロパティがあり、最初は nil
のオプショナルの tenant
プロパティがあります。アパートには常にテナントがあるとは限らないため、tenant
プロパティはオプショナルです。deinit
)も定義しています。これにより、Person
と Apartment
のインスタンスが期待どおりに割り当て解除されているかどうかを確認できます。john
と unit4A
と呼ばれるオプショナルの型の 2 つの変数を定義してます。これらは、下記の特定の Apartment
および Person
インスタンスに設定されています。これらの変数は両方とも、オプショナルなため、初期値は nil
です:Person
インスタンスと Apartment
インスタンスを作成し、これらの新しいインスタンスを john
変数と unit4A
変数に割り当てることができます:john
変数には新しい Person
インスタンスへの強参照があり、unit4A
変数には新しい Apartment
インスタンスへの強参照があります:!
)は、オプショナルの john
および unit4A
変数内に格納されているインスタンスをアンラップしてアクセスするために使用され、これらのインスタンスプロパティに値を設定できます:Person
インスタンスは Apartment
インスタンスへの強参照を持ち、Apartment
インスタンスは Person
インスタンスへの強参照を持つようになりました。したがって、john
変数と unit4A
変数で保持されている強参照を解除しても、参照カウントはゼロにはならず、インスタンスは ARC によって割り当て解除されません。nil
に設定したときには、どちらのデイニシャライザも呼び出されなかったことに注目してください。強参照循環により、Person
インスタンスと Apartment
インスタンスの割り当てが解除されることがなくなり、アプリでメモリリークが発生します。john
変数と unit4A
変数を nil
に設定した後、強参照がどのように見えるかを次に示します。Person
インスタンスと Apartment
インスタンス間の強参照は残り、なくなることはありません。Apartment
の例では、アパートがその存続期間のある時点でテナントが存在しないこともあるため、この場合、弱参照が参照循環を断ち切る適切な方法です。対照的に、他のインスタンスの存続期間が同じか、それよりも長い場合は、非所有参照を使用します。weak
キーワードを配置することにより、弱参照を示します。nil
に設定します。また、弱参照は実行時に値を nil
に変更できるようにする必要があるため、常にオプショナル型の定数ではなく変数として宣言されます。NOTE ARC が弱参照をnil
に設定した場合、プロパティオブザーバは呼び出されません。
Person
と Apartment
の例と同じですが、1 つの重要な違いがあります。今回は、Apartment
型の tenant
プロパティが弱参照として宣言されています:john
と unit4A
)からの強参照と、2 つのインスタンス間のリンクは、以前と同じように作成されます。Person
インスタンスは依然として Apartment
インスタンスへの強参照を持っていますが、Apartment
インスタンスは Person
インスタンスへの弱参照を持っています。これは、john
変数を nil
に設定してその強参照を解除すると、Person
インスタンスへの強参照がなくなることを意味します。Person
インスタンスへの強参照がなくなったため、割り当てが解除され、tenant
プロパティが nil
に設定されます:Apartment
インスタンスへの唯一の強参照は、unit4A
変数です。その強参照がなくなると、Apartment
インスタンスへの強参照はなくなります。Apartment
インスタンスへの強参照がなくなったため、この割り当ても解除されます:NOTE ガベージコレクションを使用するシステムでは、メモリプレッシャによってガベージコレクションがトリガされた場合にのみ、 強参照を持たないオブジェクトの割り当てが解除されるため、シンプルなキャッシュメカニズムを実装するために弱いポインタが使用されることがあります。ただし、ARC では、最後の強参照が削除されるとすぐに値の割り当てが解除されるため、弱参照はそのような目的には適していません。
unowned
キーワードを配置することにより、非所有参照を示します。弱参照とは異なり、非所有参照は常に値を持つことが期待されます。その結果、値を unowned
としてマークしてもオプショナルにはなりません。また、ARC は非所有参照の値を nil
に設定することはありません。NOTE 割り当て解除されないインスタンスを参照していることが確実な場合にのみ、非所有参照を使用してください。
Customer
と CreditCard
を定義しています。これらのクラスは、銀行の顧客と、その顧客のクレジットカードをモデル化しています。これら 2 つのクラスはそれぞれ、もう一方のクラスのインスタンスをプロパティとして保持しています。この関係は、強参照循環を生む可能性があります。Customer
と CreditCard
の関係は、上記の弱参照の例に見られる Apartment
と Person
の関係とは少し異なります。このデータモデルでは、顧客はクレジットカードを持っている場合と持っていない場合がありますが、クレジットカードは常に顧客に関連付けられます。CreditCard
インスタンスは、それが参照する顧客より長生きすることはありません。これを表すために、Customer
クラスにはオプショナルの card
プロパティがありますが、CreditCard
クラスには(オプショナルではない)非所有(unowned
)の顧客プロパティがあります。CreditCard
インスタンスは、数値と顧客インスタンスを独自の CreditCard
イニシャライザに渡すことによってのみ作成できます。これにより、CreditCard
インスタンスの作成時に、CreditCard
インスタンスに常に顧客インスタンスが関連付けられます。NOTECreditCard
クラスのnumber
プロパティは、Int
ではなくUInt64
の型で定義され、number
プロパティの容量は 32 ビットと 64 ビットの両方のシステムで 16 桁のカード番号を格納するのに十分な大きさです。
john
というオプショナルの Customer
変数を定義します。この変数は、特定の顧客への参照を保存するために使用されます。この変数はオプショナルのため、初期値は nil
です:Customer
インスタンスを作成し、それを使用して新しい CreditCard
インスタンスを初期化し、その顧客の card
プロパティとして割り当てることができます:Customer
インスタンスには、CreditCard
インスタンスへの強参照があり、CreditCard
インスタンスには、Customer
インスタンスへの非所有参照があります。customer
は非所有参照のため、john
変数によって保持されている強参照を解除すると、Customer
インスタンスへの強参照はなくなります:Customer
インスタンスへの強参照がなくなったため、割り当てが解除されます。これが発生すると、CreditCard
インスタンスへの強参照もなくなり、割り当ても解除されます。john
変数が nil
に設定された後、Customer
インスタンスと CreditCard
インスタンスのデイニシャライザが両方の"deinitialized"メッセージを出力することを示しています。NOTE 上記の例は、安全な非所有参照の使用方法を示しています。Swift は、パフォーマンス上の理由などで、ランタイムの安全性チェックを無効にする必要がある場合に、安全でない非所有参照も提供します。他の全ての安全でない操作と同様、そのコードの安全性をチェックする責任はあなたにあります。unowned(unsafe)
と書くことで、安全でない非所有参照を示します。参照しているインスタンスの割り当てが解除された後で、所有されていない安全でない参照にアクセスしようとすると、プログラムはインスタンスがかつて存在していたメモリアドレスにアクセスしようとしますが、これは安全ではありません。
unowned
とマークできます。ARC 所有権モデルに関しては、オプショナルの非所有参照と弱参照の両方を同じコンテキストで使用できます。違いは、オプショナルの非所有参照を使用する場合、それが常に有効なオブジェクトを参照しているのか、nil
が設定されているのかを確認する責任があることです。Department
は、学科が提供する各コースへの強参照を維持します。ARC 所有権モデルでは、学科がそのコースを所有します。Course
には非所有参照が 2 つあります。1 つは学科へ、もう 1 つは学生が受講する必要がある次のコースです。コースはこれらのオブジェクトのいずれも所有しません。全てのコースはいくつかの部門の一部のため、department
プロパティはオプショナルではありません。ただし、一部のコースには推奨される次のコースがないため、nextCourse
プロパティはオプショナルです。nextCourse
プロパティに次のコースの提案があり、このコースを完了した後に学生が受講する必要があるコースへのオプショナルの非所有参照が保持されます。nil
にできることを除いて、ARC での非所有参照と同じように動作します。nextCourse
が常に割り当てが解除されていないコースを参照するようにする必要があります。この場合、例えば、department.courses
からコースを削除するときに、他のコースが持つ可能性のあるそのコースへの参照も全て削除する必要があります。NOTE オプショナル値の基になる型はOptional
です。これは、Swift 標準ライブラリの列挙型です。Optional
は、値型をunowned
でマークできないという規則の例外です。クラスをラップするオプショナルは ARC を使用しないため、オプショナルへの強参照を維持する必要はありません。
Person
と Apartment
の例は、両方とも nil
にすることができる 2 つのプロパティが、強参照循環を引き起こす可能性がある状況を示しています。このシナリオは、弱参照で解決するのが最善です。Customer
と CreditCard
の例は、nil
にできる 1 つのプロパティと nil
にできない別のプロパティが、強参照循環を引き起こす可能性がある状況を示しています。このシナリオは、非所有参照で解決するのが最善です。nil
にはできません。このシナリオでは、一方のクラスに unowned
プロパティ、もう一方のクラスに暗黙アンラップオプショナルプロパティを使用すると便利です。Country
と City
の 2 つのクラスが定義されており、それぞれが他のクラスのインスタンスをプロパティとして保持しています。このデータモデルでは、全ての国には常に首都が必要で、全ての都市は常に国に属している必要があります。これを表すために、Country
クラスには capitalCity
プロパティがあり、City
クラスには country
プロパティがあります:City
のイニシャライザは Country
インスタンスを取得し、このインスタンスを country
プロパティに格納します。City
のイニシャライザは、Country
のイニシャライザ内から呼び出されます。ただし、Two-Phase Initialization(2 段階の初期化)で説明されているように、新しい Country
インスタンスが完全に初期化されるまで、Country
のイニシャライザは self
を City
イニシャライザに渡すことはできません。Country
の capitalCity
プロパティを、型注釈の最後に感嘆符で示される、暗黙アンラップオプショナルプロパティ(City!
)として宣言しています。これは、他のオプショナルと同様に、capitalCity
プロパティのデフォルト値は nil
ですが、Implicitly Unwrapped Optionals(暗黙アンラップオプショナル)で説明されているように、その値をアンラップする必要なくアクセスできることを意味します。capitalCity
にはデフォルトの nil
値があるため、新しい Country
インスタンスは、イニシャライザ内で name
プロパティを設定するとすぐに完全に初期化されたと見なされます。これは、name
プロパティが設定されるとすぐに、Country
イニシャライザが暗黙の self
プロパティの参照の受け渡しを開始できることを意味します。そして、Country
イニシャライザが自身の capitalCity
プロパティを設定するときに、self
を City
イニシャライザのパラメータの 1 つとして渡すことができます。Country
と City
のインスタンスを作成できることを意味します。また、オプショナルの値をアンラップするために感嘆符を使用する必要もなく、capitalCity
プロパティに直接アクセスできます:capitalCity
プロパティは、初期化が完了すると、強参照循環を回避しつつ、オプショナルではない値のように使用およびアクセスできます。self.someProperty
など) にアクセスした場合、またはクロージャがインスタンスのメソッド(self.someMethod()
など)を呼び出した場合に発生する可能性があります。いずれの場合も、これらのアクセスにより、クロージャが self
を「キャプチャ」し、強参照循環が作成されます。self
を参照するクロージャを使用する場合に強参照循環を作成する方法を示しています。この例では、HTMLElement
というクラスを定義しています。これは、HTML ドキュメント内の個々の要素にシンプルなモデルを提供します:HTMLElement
クラスは、見出し要素の場合は "h1"
、段落要素の場合は "p"
、改行要素の場合は "br"
など、要素の名前を示す name
プロパティを定義しています。HTMLElement
は、オプショナルの text
プロパティも定義しています。このプロパティは、その HTML 要素内でレンダリングされるテキストを表す文字列に設定できます。HTMLElement
クラスは asHTML
と呼ばれる遅延プロパティを定義しています。このプロパティは、name
と text
を HTML 文字列フラグメントに結合するクロージャを参照しています。asHTML
プロパティの型は () -> String
、または「パラメータを受け取らず、String
値を返す関数」です。asHTML
プロパティには、HTML タグの文字列表現を返すクロージャが割り当てられます。このタグには、(存在する場合)オプショナルの text
値が含まれます。text
が存在しない場合、テキストコンテンツは含まれません。段落要素の場合、クロージャは text
プロパティが "some text"
または nil
かどうかに応じて、"<p>some text</p>"
または "<p />"
を返します。asHTML
プロパティには名前が付けられ、インスタンスメソッドのように使用されます。ただし、asHTML
はインスタンスメソッドではなくクロージャプロパティのため、特定の HTML 要素の HTML レンダリングを変更する場合は、asHTML
プロパティのデフォルト値を独自のクロージャに置き換えることができます。asHTML
プロパティは、text
プロパティが nil
の場合にデフォルトで何らかのテキストになるクロージャを設定できます。NOTEasHTML
プロパティは遅延プロパティとして宣言されています。これは、要素が実際に HTML の出力ターゲットの文字列値の一部としてレンダリングされる場合にのみ必要になるためです。asHTML
が遅延プロパティであるということは、デフォルトクロージャ内でself
を参照できることを意味します。これは、初期化が完了し、self
が存在することがわかるまで遅延プロパティにアクセスできないためです。
HTMLElement
クラスは単一のイニシャライザを提供します。このイニシャライザは、新しい要素を初期化するための name
引数と(必要に応じて)text
引数を受け取ります。このクラスは、HTMLElement
インスタンスの割り当てが解除されたときに表示するメッセージを出力するデイニシャライザも定義しています。HTMLElement
クラスを使用して新しいインスタンスを作成および出力する方法は次のとおりです:NOTE 上記のparagraph
変数はオプショナルのHTMLElement
として定義されているため、下記ではnil
に設定して、強参照循環の存在を示すことができます。
HTMLElement
クラスは、HTMLElement
インスタンスと、デフォルトの asHTML
値に使用されるクロージャとの間に強参照循環を作成します。循環は次のようになります:asHTML
プロパティは、そのクロージャへの強参照を保持します。ただし、クロージャは(self.name
および self.text
を参照する方法として)本文内の self
を参照するため、クロージャは self
をキャプチャします。つまり、クロージャは HTMLElement
インスタンスへの強参照を保持します。2 つの間に強参照循環が作成されます。(クロージャでの値のキャプチャの詳細については、Capturing Values(値のキャプチャ)を参照してください)NOTE クロージャはself
を複数回参照していますが、HTMLElement
インスタンスへの強参照は 1 つだけです。
paragraph
変数を nil
に設定し、HTMLElement
インスタンスへの強参照を解除しても、強参照循環のため、HTMLElement
インスタンスもそのクロージャも割り当てを解除できません。HTMLElement
のデイニシャライザのメッセージは出力されないことに注目してください。HTMLElement
インスタンスが割り当て解除されていないことを示しています。NOTE Swift では、クロージャ内でself
のメンバを参照するときは常に、(someProperty
またはsomeMethod()
だけでなく)self.someProperty
またはself.someMethod()
と記述する必要があります。これは、気づかずにself
をキャプチャする可能性があることを防ぐのに役立ちます。
weak
または unowned
キーワードと、クラスインスタンス(self
など)への参照、または何らかの値で初期化された変数(delegate = self.delegate
など)とのペアです。これらのペアは、カンマで区切られた 1 組の角括弧内([]
)に記述されます。in
キーワードを置きます。nil
になります。これにより、クロージャの本文内に存在するかどうかを確認できます。NOTE キャプチャされた参照がnil
にならない場合は、弱参照ではなく、常に非所有参照としてキャプチャする必要があります。
HTMLElement
の例を解決するために適切なキャプチャ方法です。循環を回避するための HTMLElement
クラスの作成方法は次のとおりです:HTMLElement
の実装は、asHTML
クロージャ内にキャプチャリストが追加されていることを除けば、前の実装と同じです。この場合、キャプチャリストは [unowned self]
で、「強参照ではなく、非所有参照として self
をキャプチャする」という意味です。HTMLElement
インスタンスを作成して出力できます。self
のキャプチャは非所有参照で、キャプチャした HTMLElement
インスタンスを強く保持しません。paragraph
変数からの強参照を nil
に設定すると、HTMLElement
インスタンスの割り当てが解除されます。これは、下記の例のデイニシャライザメッセージの出力からもわかります: