#値の受け渡し方法の分類(値型と参照型)
値の受け渡し方法は2つある。
###値型
変更を他の変数や定数と共有しない。
→構造体(struct)、列挙型(enum)は値型として実装されている。
###参照型
変更を他の変数や定数と共有する。
→クラス(class)は参照型として実装されている。
#値型
インスタンスが値への参照ではなく、値そのものを表す型。
複数の変数や定数で1つの値型のインスタンスを共有することはできない。
変数、定数への代入や関数への受け渡しのたびにコピーを行っている。
例1
var a = 4.0 // aに4.0が入る
var b = a //bに4.0が入る(aが持つ4.0への参照ではなく値である4.0が入る)
a.formSquareRoot() //aの平方根を取る
a //aは2.0になる
b //bはaの変更の影響を受けずに4.0のままとなる
例2
struct Color {
var red: Int
var green: Int
var blue: Int
}
var a = Color(red: 255, green: 0, blue: 0) //aに赤を代入
var b = a //bに赤を代入
a.red = 0 //aを黒に変更する
//aは黒になる
a.red //0
a.green //0
a.blue //0
//bは赤のまま
b.red //255
b.green //0
b.blue //0
###mutatingキーワード 自身の値の変更を宣言するキーワード
(mutate 訳:変異する)
mutatingキーワードをメソッド(func)の宣言に追加することで、自身の値を変更する処理を実行できる。mutatingキーワードが指定されたメソッドを実行してインスタンスの値を変更すると、インスタンスが格納されている変数への暗黙的な再代入が行われる。
再代入のため、定数(let)に対しては実行できず、コンパイルエラーとなる。
例
extension Int { //extension(拡張)を使って、Int型にメソッドを追加
mutating func increment() { //mutatingキーワードを指定
self += 1 //自身の値に1を加算する
}
}
var a = 1 //1
a.increment() //2(暗黙的にaに再代入が行われている)
let b = 1
b.increment() //bに再代入できないため(letだから)コンパイルエラー
#参照型
複数の変数や定数で1つの参照型のインスタンスを共有する。
そのため、ある値に対する変更はインスタンスを共有しているほかの変数や定数にも影響する。
値型と違って、変数や定数への代入時や関数への受け渡し時にはインスタンスのコピーが発生せず、効率的なインスタンスの受け渡しができるというメリットがある。
例
class IntBox {
var value: Int
init(value: Int) {
self.value = value
}
}
var a = IntBox(value: 1) //aはIntBox(value:1)を参照する
var b = a //bはaと同じインスタンスを参照する
//a.value、b.valueは両方とも1
a.value //1
b.value //1
//a.valueを2に変更する
a.value=2
//a.value、b.valueは両方とも2
a.value //2
b.value //2
#値型と参照型の使い分け
基本は値型を使い、変更の共有が必要となるところは参照型を使う。
値型は、変数や定数への代入や引数への受け渡しのたびにコピーされ、変更は共有されない。
→一度代入された値は明示的に再代入しない限りは不変であることが保証される。
参照型は、変数や定数への代入や引数への受け渡しの際にコピーされずに参照が渡されるため、変更が共有される。
→一度代入された値が変更されないことの保証は難しい。
よって、安全にデータを取り扱うためには積極的に値型を使い、参照型は状態管理など変更の共有が必要となる範囲のみにとどめるのが理想。