🐦

Swiftの値型と参照型、はじめからていねいに

2020/12/05に公開約2,600字

Swiftのデータ型

Swiftではクラスのイニシャライザによって生成されたクラスの実体の他に、整数や構造体などの他のデータ型も全てインスタンスと称します。そして、インスタンスの型には値型と参照型の2種類があります。

値型

値型 (value type)とは代入や関数に引数として渡される際に値がコピーされるデータ型です。代入や演算によって前のデータは変更されません。Swiftにおいてはほとんど全て(クラスとクロージャ以外)のデータ型が値型です。[1]

var a = 0 //整数(Int型)は値型のデータ
var b = a //aの値をコピーしてbに代入

b = 1

print("a:\(a)")
print("b:\(b)")

---
a:0 //b = 1 によって変更は受けない
b:1

参照型

参照型 (refrence type)とは代入や関数に引数として渡される際に値そのものではなく、データ自体への参照をを渡すデータ型です。ここでの参照とはデータ自体が保存されているメモリ上の領域のアドレスのことを指します。代入や関数に渡された後の演算操作は渡された参照をもとに辿って代入した側のインスタンスに対して行われます。よって、代入した側もそれらの操作による変更を受けます。Swiftにおいてはクラスとクロージャが参照型のデータ型です。

//クラス定義(クラスは参照型のデータ)
class Artist {
    var genre: String
    
    init(genre: String) {
        self.genre = genre
    }
}

var a = Artist(genre: "rock") //genreをrockとして生成
var b = a //aの参照(メモリ上のアドレス)をコピーして代入

b.genre = "pop" //b(=aから渡された参照から辿ることのできるa自体)のgenreを書き換える

print("a:\(a.genre)")
print("b:\(b.genre)") //b(=aから渡された参照から辿ることのできるa自体)のgenreを出力する

---
a:pop
b:pop

データ型とlet

上記の内容は最初は混乱しやすい部分でもありますが、値型と参照型の2種類があるということが事前に分かっていれば、理解するのに難しくない挙動だと思います。しかし、同じlet宣言でも値型のデータ型に対するlet宣言とデータ型に対するlet宣言では意味合いが少し異なります。以下では値型の構造体 (struct)と参照型のクラスを使って、両者の挙動の違いをまとめます。

値型の場合

値型のデータ型でlet宣言を行なった場合、その定数に保存された値自体が書き換えられないようになります。

以下の例では、構造体Hoge型のインスタンス hogeをletで宣言したのちにプロパティaを書き換えようとしますが、これは値型の定数の値を書き換えようとしているためエラーになります。

//構造体定義
struct Hoge {
    var a: UInt8
    var b: UInt8
}


let hoge = Hoge(a: 100, b: 200) //インスタンス生成
foo.a = 5 // Cannot assign to property: 'hoge' is a 'let' constant

参照型の場合

一方、参照型の場合、データ自体にはインスタンスに対する参照が格納されるため、参照型のデータ型に対するlet宣言は参照の書き換えに対して効力があります。すなわち、参照自体の書き換えができないものの、クラスが持つプロパティの書き換えに対して効力があるわけではありません。したがって、あるクラスのインスタンスがlet宣言で生成されても、そのインスタンスが保持するプロパティの書き換えをすることができます。以下の例をみてください。

//クラス定義
class Fuga {
    var a: UInt8
    var b: UInt8
    
    init(a: UInt8, b: UInt8) {
        self.a = a
        self.b = b
    }
}

let fuga = Fuga(a: 100, b: 200) //インスタンス生成
fuga.a = 5 //書き換えができる

終わりに

iOSアプリの開発環境であるXcodeはエラー補完の機能が充実しているので、ここら辺の内容をあまり理解していなくてもfixボタンをポチポチ押して行けば、文法的に破綻していないコードをひとまず書くことができてしまいます。しかし、ここら辺の原理原則を正しく理解していれば、予期せぬエラーに頭を悩ませることは少ないですし、コード全体の見通しも良くなりますね。

参考

脚注
  1. 正確には列挙体(enum)と構造体(struct)が値型として定義されていて、内部的にstructを基に実装されている基礎的なデータ型が値型になるため、ほとんどの型が値型ということになります。 > You’ve actually been using value types extensively throughout the previous chapters. In fact, all of the basic types in Swift—integers, floating-point numbers, Booleans, strings, arrays and dictionaries—are value types, and are implemented as structures behind the scenes. ↩︎

Discussion

ログインするとコメントできます