この投稿は...
Swiftのクラスで行われる 参照渡し について、解説します。
Swiftを基礎から学ぶには
自著、工学社より発売中の「まるごと分かるSwiftプログラミング」をお勧めします。変数、関数、フロー制御構文、データ構造はもちろん、構造体からクロージャ、エクステンション、プロトコル、クロージャまでを基礎からわかりやすく解説しています。
また、Swiftプログラミングを基礎から動画で学びたい方には、Udemyコース「今日からはじめるプログラミング」をお勧めします。
クラスは参照型データである
Swfitでは、クラスは参照型データの分類されます。
構造体のような値型データとは異なり、参照型データは変数や定数あるいは関数に割り当てられたときに「インスタンスのコピー」が発生しません。
インスタンスのコピーではなく、インスタンスへの参照 が渡されます(参照渡し)。
例として、ホームスピーカーのボリューム調節を考えます。
このホームスピーカーは家全体にひとつだけ設置されており、ボリュームは部屋のリモコンから操作できます。
つまり、すべてのリモコンは「共通のホームスピーカー1台」だけを操作します。
ホームスピーカーをモデル化するHomeSpeaker
クラスは、次のように定義します。
class HomeSpeaker {
var volume: Int = 0
}
let livingRoom = HomeSpeaker()
livingRoom.volume = 25
上のコードは、リビングルームからホームスピーカーを操作できるようにしたことを意味します。
そして、ボリュームを25
に設定します。
このとき、livingRoom
定数に設定されているのは「新たに作成されたHomeSpeaker
インスタンスへの参照」です。
次に、livingRoom
定数を新しいtheaterRoom
定数に割り当てます。
そして、theaterRoom
のボリュームを30
に変更します。
let theaterRoom = livingRoom
theaterRoom.volume = 30
このとき、theaterRoom
定数に設定されるのは「以前に作成したHomeSpeaker
インスタンスへの参照」です。
つまり、livingRoom
定数とtheaterRoom
定数は、両方とも「同一のHomeSpeaker
インスタンス」を参照します。
要するに、「異なる2つの名前」で同一のインスタンスにアクセスしている状態です。
それを確認するために、livingRoom
定数のvolume
プロパティにアクセスしてみましょう。
print("The volume of living room is now \(livingRoom.volume).")
// Prints "The volume of living room is now 30."
すると、シアタールームのボリュームに設定した30
が、リビングルームにも影響していることがわかります。
この例は、参照型データの値について、いくつかの重要な点を示唆しています。
-
ひとつは、値が予測しにくいことです。仮に、
livingRoom
定数とtheaterRoom
定数が離れてバラバラに存在していると、ボリュームを変更するすべてのコードを見つけるのは困難です。livingRoom
定数を使用するときは常に、theaterRoom
定数のことも考慮しなければいけません。これは、家中にリモコンがあると「誰がどこからホームスピーカーを操作するか」を予測できないことと似ています。 -
もうひとつは、
livingRoom
とtheaterRoom
が定数として宣言されている点です。定数なのに、どちらもvelume
プロパティの値を変更できました。それは、これらの定数が保持しているのは「インスタンス自体」ではなく、「インスタンスへの参照」だからです。つまり、インスタンスのvolume
プロパティを変更しても、定数が「どのインスタンスを参照するか」は変わりません。実際に変更されるのは「インスタンスへの参照」ではなく、参照先にあるHomeSpeaker
インスタンスのvolume
プロパティです。