Swiftのクラスのインスタンスは参照型、構造体のインスタンスは値型だというのは、ご存じでしょう。
クラスは実態が1つ、構造体は都度実態が生成されるといった感じです。
ではその理屈でいくと、配列の値がクラスだったり構造体だったりした場合に、配列内に入れたオブジェクトをいじる時、構造体だと参照型だから一度変数に入れてその後差し替えなくちゃいけないの?となりますよね。
意外と混乱しやすいポイントかと思って、検証コードを書いてみました。
クラスの場合
class Hoge {
var fuga = 0
}
let array = [Hoge(), Hoge()] // letでOK
// forEachで配列データをいじる
array.forEach { $0.fuga = 1 }
array // [{fuga 1}, {fuga 1}]
// mapで配列のデータをいじる
let _ = array.map { $0.fuga = 2 }
array // [{fuga 2}, {fuga 2}]
// 直接いじる
array[1].fuga = 3
array // [{fuga 2}, {fuga 3}]
forEach、mapとも取得したインスタンスの変数に直接値を入れても、参照型なので元のデータが変わるのが確認できます。
array自体もletで問題ありません。
構造体の場合
struct Hoge {
var fuga = 0
}
var array = [Hoge(), Hoge()] // varにして配列自体を変更可能にしておく
// forEachで配列データをいじる (元の値は変わらない)
array.forEach { (var data) in
data.fuga = 1
}
array // [{fuga 0}, {fuga 0}]
// mapで配列のデータをいじる (入れ替える形になる)
array = array.map { (var data) in
data.fuga = 2
return data
}
array // [{fuga 2}, {fuga 2}]
// 直接いじる
array[1].fuga = 3
array // [{fuga 2}, {fuga 3}]
構造体の場合、まずarrayをvarで宣言します。letだと配列の中身が構造体の場合なにも変更できません。
forEachでは、渡されるデータの変更はエラーになりできません。では渡されるデータをvarにしたらどうなるかというと、そのキャプチャーのみ変更できるだけで、元のデータには影響しません。
mapでも同様で渡されたデータをいじっても元のデータには影響しません。上記のコードでは値を変更した構造体を返しているので、あらたに配列が生成されているのと同じことになります。
最後に配列を直接いじってみましょう。直接元の構造体のインスタンスをいじるのと同じなので、変更は反映されました。
forEachもmapもクロージャに入ってくる値はコピーされた値だということですね。
どちらも一長一短です。
クラスのほうが楽にみえますが、意図せずmapのクロージャ内でデータをいじってしまい、元の値が変わったのに気がつかないというパターンも考えられます。
この特性を覚えておいて、適材適所で使い分けるようにしましょう。