目次
はじめに
Swiftには構造体、クラス、列挙型という3つの型の種類がありますが、これらは値の受け渡しの方法によって値型と参照型の2つに大別できます。今回はこの値型と参照型について説明したいと思います。
値型
値型は値の受け渡しをした変数や定数と変更を共有しない型です。
なぜなら、値型のインスタンスは値への参照ではなく、値そのものを表すからです。つまり、変数や定数への値型のインスタンスの代入は、インスタンスが表す値そのものの代入を意味するため、複数の変数や定数で1つの値型のインスタンスを共有することはできません。
値型の代表例として、Int型を使ってみます。(標準ライブラリで提供されている数値型、String型Bool型などは値型となっております。)
var a = 5
var b = a // bにaの値を代入
// インスタンスは共有されないため、aの値を変更してもbの値は変わらない。
a = a * 2
print(a) // 10
print(b) // 5
したがって、一度代入したインスタンスは再代入を行わない限り不変であるため、その値が予測可能になるというメリットがありますが、変数や定数への代入時や関数への受け渡し時には毎回インスタンスのコピーが発生するため、参照型に比べ非効率なインスタンスの受け渡しをするというデメリットがあります。
Swiftでは構造体と列挙型が値型です。
struct TriangleRatio {
var bottom: Double
var height: Double
var hypotenuse: Double
}
var a = TriangleRatio(bottom: 1, height: 1, hypotenuse: 1) // aに正三角形を代入
var b = a // bにaの値を代入
a.hypotenuse = sqrt(2) // aを二等辺三角形にする
// aは二等辺三角形になるが、bは正三角形のまま
print(a) // TriangleRatio(bottom: 1, height: 1, hypotenuse: √2)
print(b) // TriangleRatio(bottom: 1, height: 1, hypotenuse: 1)
mutatingキーワード
値型では、mutatingキーワードをメソッドの宣言に追加することで、自身の値を変更する処理を実行できます。mutatingキーワードが指定されたメソッドを実行してインスタンスの値を変更すると、インスタンスが格納されている変数への暗黙的な再代入が行われます。
mutatingキーワードが指定されたメソッドの呼び出しは再代入として扱われるため、定数に格納された値型のインスタンスに対しては実行できません。
extension Int {
mutating func increment() {
self += 1
}
}
var a = 1
a.increment() // 2
let b = 1
b.increment(). // bは定数であるため、再代入できずコンパイルエラー
参照型
参照型は値の受け渡しをした変数や定数と変更を共有する型です。
なぜなら、参照型のインスタンスは値そのものではなく、値への参照を表すからです。つまり、変数や定数への参照型のインスタンスの代入は、インスタンスに対する参照の代入を意味するため、複数の変数や定数で1つの参照型のインスタンスを共有することができます。
したがって、一度代入したインスタンスは他の参照している変数や定数からも変更可能であるため、その値が予測し難くなるというデメリットがありますが、変数や定数への代入時や関数への受け渡し時にはインスタンスのコピーが発生しないため、値型に比べ効率的なインスタンスの受け渡しができるというメリットがあります。
Swiftではクラスが参照型です。
class TriangleRatio {
var bottom: Double
var height: Double
var hypotenuse: Double
init(bottom: Double, height: Double, hypotenuse: Double) {
self.bottom = bottom
self.height = height
self.hypotenuse = hypotenuse
}
}
var a = TriangleRatio(bottom: 1, height: 1, hypotenuse: 1) // aに正三角形を代入
var b = a // bにaの値を代入
a.hypotenuse = sqrt(2) // aを二等辺三角形にする
// aもbも二等辺三角形になる
print(a) // TriangleRatio(bottom: 1, height: 1, hypotenuse: √2)
print(b) // TriangleRatio(bottom: 1, height: 1, hypotenuse: √2)
メモリ管理
値型のメモリ管理
値型は、インスタンスを代入した変数(定数)が使われなくなると(=スコープの範囲外に出ると)、インスタンスのメモリは自動的に解放されます。そのため値型に関してはメモリ管理を考える必要はありません。
参照型のメモリ管理
参照型は、上記で説明したように複数の箇所から1つのメモリを参照するので、使われなくなったかどうかを単純に判定することができません。そこでSwiftでは、ARC(Automatic Reference Counting)という方式を用いて、メモリ領域を自動的に管理してくれます。
ARCでは、使用中のインスタンスのメモリが解放されてしまうことを防ぐために、プロパティ、変数、定数からそれぞれのインスタンスへの参照がいくつあるかをカウントしています。このカウントが0になったとき、そのインスタンスはどこからも参照されていない(どこからも使われていない)とみなされ、メモリが解放されます。このカウントのことを参照カウントと呼びます。
詳しいメモリ管理に関しての説明は、別途記事を作成したいと思います。
参考
・Swift実践入門 (https://gihyo.jp/book/2020/978-4-297-11213-4)