本日は Automatic Reference Counting
の実際の動きを The Swift Programming Language に沿って整理していきます。前回とその前を通して Automatic Reference Counting
の基本的な仕組みを Objective-C や Playground を使って眺めてきた様子を振り返りながらスライドに沿って再確認していってみようと思います。どうぞよろしくお願いしますね。
—————————————————————————— 熊谷さんのやさしい Swift 勉強会 #211
00:00 開始 00:33 ARC の挙動を再確認 01:06 ARC はクラスに対する機能 02:46 ARC の具体例 03:05 本来は構造体で作るべき型 03:51 クラスと構造体の性格イメージ 04:40 イミュータブルクラス 05:44 ARC が働いたことを知るための仕掛け 06:58 意図したタイミングで解放可能な変数を用意 07:34 オプショナル型の既定値は nil 08:29 ARC の挙動観察 09:24 強参照と弱参照 10:33 ARC の強み 12:16 John Appleseed 13:44 参照カウントを増加させる 14:46 具体的な参照のされ方 19:04 対して、構造体の参照?のされ方 20:16 具体的に self を捉える 22:11 参照と初期化の様子 22:43 クロージングと次回の展望 ——————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #211
では始めていきますね。今日は、Automatic Reference Counting(ARC)について話していきます。前回もARCを紹介しましたが、そのときは概要だけで、やや捉えにくいと感じたかもしれません。
今回は、ARCの挙動を実際のコードを使って見ていこうということです。具体的なコードを追いかけるのは前回もやりましたが、その振り返りとして取り組んでいきます。どのように取り組めば分かりやすくなるかを考えながら見ていきたいと思います。
重要なのは、ARCがクラスに注目した仕組みだということです。Swiftでは、コード対認識が向きがちなところがありますが、クラスを使うことは少ないかもしれません。しかし、ARCはクラスのメモリー管理に関するお話です。
クラスは、メモリーを割り当てたときに参照を通じて色々な変数から共有されることが多いです。そのため、誰かがそのクラスインスタンスを参照している限り、そのインスタンスはメモリー上に存続しなければなりません。そうしないと、不正アクセスでアプリがクラッシュする可能性があります。
Swiftは、この参照カウント(Reference Counting)という方式を使ってメモリーの管理を行います。そして、この参照カウントを自動で管理しているのがARCという仕組みです。つまり、ARCは参照カウントを自動で管理し、メモリーの解放を行ってくれるというわけです。
では、ARCがどのように動くか具体例を見ていきましょう。まずは、単純なクラス「Person」を定義します。このクラスには定数プロパティ name
を持たせます。ARCの話をするためには、クラスを使わないといけないのです。
なお、Swiftでは値を表現する場合、構造体を使うことが一般的です。値を表現する「Person」は、構造体で作ることが多いですね。構造体とクラスの違いについては以前も話しましたが、改めて言うと、構造体は値を表現するもので、クラスは状態を管理するものです。
クラスをもう少し説明すると、クラスは時間とともに変化する状態を管理するために使われます。一方、構造体は主に値の表現に使われます。この違いに関しては、言語仕様に適した用途で使い分けることが重要です。
ここで、クラスとして「Person」を定義し、定数プロパティ name
を持たせました。このようにクラスで定数プロパティを持たせると、イミュータブルクラス(不変クラス)を作ることができます。イミュータブルクラスは値を表現するのに近いですが、オブジェクト指向で安全に値を扱うアプローチとして存在しています。
Swiftでこのようなイミュータブルクラスを作ることが大事です。もちろん、構造体を使うのも一つの手ですが、今回はクラスを使ってARCの紹介をしていきます。それでは、具体的なコード例を見て、ARCの動きを確認していきましょう。 まず、パーソンのイニシャライザーとしてプロパティを初期化し、プロパティを保持します。そして、イニシャライズされたことがプリントされるアプリを作成します。さらに、パーソンインスタンスが解放されたことをプリントするメッセージを表示します。これにより、イニシャライズと解放のタイミングを確認できます。ARC(Automatic Reference Counting)が正しく動作しているかどうかを把握するためのサンプルです。
次に、解説に入ります。パーソンクラスは name
プロパティをセットし、イニシャライズされたことを示すメッセージを出力します。この際の出荷が進行中であることを示すメッセージについては、実際には「出荷されたよ」というメッセージが表示された気がします。いずれにせよ、解放されたときにきちんとメッセージが表示されるイニシャライザーを持っています。
さて、ここで3つのオプショナル型のパーソン変数を用意しました。これを使ってARCの動きを見ていきます。これらの変数はオプショナル型なので自動的に nil
で初期化され、現時点ではパーソンインスタンスを参照していません。通常のオプショナル型でも何も代入されていないと初期値は nil
となるため、 = nil
と書いているのと同じです。要点としては、パーソン型の変数はまだ何も参照していないことを押さえておいてください。
これで実際にチェックすることが可能になりました。まずはスライドを読み、その後でプレイグラウンド上でどのような動きをするかを確認します。
次に、リファレンス1変数に新たにパーソンインスタンスを生成したことを示します。このパーソンクラスのイニシャライザーが呼ばれるタイミングで 'initialized'
というメッセージが表示されます。これにより、パーソンインスタンスがリファレンス1に割り当てられたために、強い参照が生成されました。
強い参照とは、オートマチックリファレンスカウンティング(ARC)がそのインスタンスを参照している状態です。参照カウントを増やすことになります。逆に弱参照というのは、ARCにおいて参照カウントを増やさない参照の仕方です。今回は標準的な強参照を作成したことになります。
これによって、少なくとも一つの強参照が存在するため、ARCはそのインスタンスが解放されないように制御することになります。前々回のObjective-CのARCについての話では、ARCを無効化して手動でメモリ管理を行っていましたが、現在ではARCにより非常に簡単に管理できます。
以上が、ARCの基本的な概要とその動作の一例です。 なお、ARC周りの知識は忘れがちなので、改めて学習していかないと難しい分野です。こうやって学習の機会があると良いですね。
ちなみに、John Appleseedという名前が出てきましたが、調べてみたらちゃんと理由があるんです。それほど大げさではないですが、このJohn Appleseedという名前は、AppleがiPhoneの製品画像などでよく使う名前で、元々はダミーの名前です。もともとは「ジョニー・アップルシード」という方がいて、その名前を用いたものです。ジョニー・アップルシードは、1774年から1845年のアメリカの開発者で、リンゴの種を配って歩いたというアメリカの民話の人物のモデルです。この民話の詳細までは調べていませんが、そういった名前で、Appleの会社が愛着を持ってJohn Appleseedという愛称にして使っています。どうでもいいことかもしれませんが、次の話題に進みましょう。
次は参照カウントについてです。先ほど、新しいインスタンスを持たせた変数リファレンス1
を使いましたが、これをさらにリファレンス2
とリファレンス3
に代入すると、代入するたびにリファレンス1
に入っているインスタンスが他の二か所からも参照されたことになります。つまり、参照カウントがさらに2増えて、合計3になったという状況をARC(Automatic Reference Counting)が暗黙的に把握してくれているのです。イコール文を書くだけで、裏でそういう処理が行われているのは非常に興味深いですね。
もう少し話を進めますが、一度補足しておきましょう。例えば、リファレンス1
がリファレンス2
となったときに、リファレンス1
をリファレンス2
に代入する場合を考えます。新たに入れ物を用意し、その上でインスタンスを一つ新たに作成するというわけです。
参照型なのでリファレンス1
にパーソンのインスタンスが入っているとします。この「入っている」という表現は、コードの動きと直感が異なるかもしれません。仮にリファレンス1
を箱のような入れ物だと考え、この入れ物の中にX
が直接入るわけではなく、この入れ物にはX
の場所を指し示すアドレスが入っているという状況が作られます。
初期のコードでは、リファレンス1
をリファレンス2
に代入すると、リファレンス2
の中身にリファレンス1
の内容が代入されるということです。つまり、リファレンス2
にも同じX
を指すアドレスが代入されるという状況が作られます。このときに大事になってくる価値観としては、リファレンス1
もリファレンス2
も同じX
を指しているという点です。これでX
のインスタンスが2個あるわけではなく、唯一一つのインスタンスを二箇所から参照していることになります。 なので、これがリファレンス3まで含まれた場合も同じです。リファレンス3がXを指しているとき、Xは一つの存在ですが、3カ所から参照されています。これがARCの参照の重要な考え方です。
これが構造体だと話は変わります。例えば、構造体が「パーソン」ではなく「パーソンの親」だとすると、その構造体のインスタンスを作成して代入すると、リファレンス1の中にXが入ります。次に別の代入をしても、また新しいインスタンスが生成され、その中にXが入ります。Xでの表現が難しかったのですが、「入れ物」を想定して説明します。すると、入れ物の中にXが入っている状況がわかります。入れ物が3つあるので、それぞれの中にXが入っているという感じです。
構造体の場合とクラスの場合では、この違いが大きいです。クラスの場合、3つのリファレンスから1つのオブジェクトを参照しますが、構造体の場合、それぞれのインスタンスが個別に存在します。クラスの「セルフ」はインスタンスそのものの参照を示していますが、構造体の場合はそれそのものがセルフになります。つまり、構造体のセルフを書き換えると、そのものの内容が変わります。一方、クラスのセルフを書き換えると、オブジェクト自体は変わらず、参照先が変わるだけです。このセルフの違いも特徴的で、プログラミングにおいて理解が重要です。
今回はクラスについての話なので、クラスの仕組みについて説明しました。唯一のオブジェクトが3つのリファレンスから参照されている状況を見てきました。このオブジェクトを操作したとき、ジョン・アップルシードが書き換えられるのは1回だけです。これは大きなポイントで、クラスの参照による特徴です。
次回は、この参照を解除していくとどのように動くかを見ていきます。全てのリファレンスが解除されるタイミングでARCが全てを解放してくれます。その過程について次回詳しく説明する予定です。
今日は、そろそろ時間なのでこれで終わりにしましょう。お疲れ様でした。ありがとうございました。