Swift の Class と Struct で迷った時にみる記事
Swift を書いていると、Class を使うか Struct を使うか迷う時が結構あり、どちらを使うべきか悩んだ時に判断する指標となることを簡単にまとめていきたいと思います。
参照: https://blog.usejournal.com/swift-struct-or-class-how-to-choose-d22d987bac3d
Class or Struct?
Class と Struct の最も大きな違いは、参照型か値型であるかということです。そのほかにも、オブジェクトを継承するかどうかなどの違いもありますが、両者の違いを詳しく理解するためには参照型と値型を理解することが重要になってきます。
参照型
参照型は代入時にオブジェクト自体が代入されます。つまり、A
のインスタンスを B
に代入して B
の値を変更すると、A
の値も更新されます。Swift では Class
が主に参照型として扱われます。
値型
値型は代入時にコピーが代入されます。つまり A
のインスタンスを B
に代入して B
の値を変更しても A
の値が変更されることはありません。また、Swift の値型は Copy on Write
なため、代入先で変更があった時に初めて値がコピーされます。
Swift ドキュメントの値型のセクションにもある通り、Swiftの基本的な型(integers, floating-point numbers, Booleans, strings, arrays, dictionaries) は全て値型で定義されていて、Struct や Enum で定義した型も値型になります。 docs.swift.org
これはなんで?
値型の場合、値の変更をする時に再代入が行われるため、let
で定義した値型を更新することはできません。また、struct
や enum
などで自身を再代入する際に使われる mutating func
なども同様に、let
で定義した値型で呼び出すと Error になります。
// 参照型 class Fish { var name: String init(name: String) { self.name = name } } let fish = Fish(name: "sweetfish") fish.name = "tuna" // OK
一方参照型の場合は、値型と異なり値を直接メモリ上の変数領域には格納せず、値があるアドレスを変数領域に格納するため、let
で定義したオブジェクトでもプロパティの要素が持っている値のアドレス先を要素を変更することで、変数領域を直接変更せずに値を更新することができます。また、これらは代入コストの面でも大きな恩恵を受けることができます。値型では、値を代入する際にはコピーを作成するので、データ量が多い場合はそれだけメモリを消費しますが、参照型では、アドレスのみのコピーを取得するので、本体のデータが大きくなろうと参照先は一つなので、メモリを大量に消費するコストが下がります。これについては下記の記事が大変参考になります。
// 値型 struct Fish { var name: String init(name: String) { self.name = name } } let fish = Fish(name: "sweetfish") fish.name = "tuna" // Error
どう使い分ける?
僕が実務で使い分ける際にはざっくり下記の項目で判断しています。
内容 | yes | no |
---|---|---|
状態が必要 | Class | Struct |
特定のオブジェクトを継承する | Class | Struct |
代入時にコピーを渡す | Struct | Class |
参考
- https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-ID82
- https://qiita.com/lovee/items/0bf5e663dd74983cc0d3