楽水

人々の創造が自由に表現できる舞台づくり

Swift

Swiftのクラスと構造体をわかりやすく解説

投稿日:2020年8月30日 更新日:


Swiftを学ぶ過程で、クラスと構造体は何が違うのか知りたいと考える人もいると思います。
そこで、今回は、以下の観点でSwiftのクラスと構造体について解説します。

  • クラスと構造体の定義
  • クラスと構造体の違いは何か?
  • クラスと構造体の使い分けについて

参考本
[改訂新版]Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 WEB+DB PRESS plus

クラスと構造体の定義

クラスと構造体は、プログラムコードの構成要素となる汎用的な構造です。
定数、変数、および関数とまったく同じシンタックスで、クラスと構造体に機能を追加するプロパティとメソッドを定義します。
クラスと構造体の定義シンタックスはよく似ています。
クラスは class キーワードで、構造体はstruct キーワードで始めます。共に、定義全体を波括弧内に置きます。

class SomeClass {
// クラス定義
}
struct SomeStructure {
// 構造体定義
}

以下はクラスを定義した例です。

class Shape {
 var numberOfSides = 0
 func simpleDescription() -> String {
 return “A shape with \(numberOfSides) sides.”
}

実際にクラスを使うには以下のようにクラスのインスタンスを生成する必要があります。

var shape = Shape()

以下は構造体を定義した例です。

struct Shape {
 var numberOfSides = 0
 func simpleDescription() -> String {
 return “A shape with \(numberOfSides) sides.”
}

実際に構造体を使うには以下のように構造体のインスタンスを生成する必要があります。

var shape = Shape()

クラスと構造体の違いは何か?

クラスと構造体の最も大きな違いは、構造体は値型でクラスは参照型ということです
値型は、変数や定数に代入されるとき、あるいは関数に渡されるときに、値がコピーされます。
Swift の基本的な型の整数、浮動小数点数、ブール、文字列、配列、辞書はすべて値型で、構造体として実装されています。
また、列挙型も値型です。
なので、生成した構造体や列挙型のインスタンスと、プロパティにある値は、渡されるときに常にコピーされます。
上記構造体を使った次の例を見てください。

var shape = Shape()
shape.numberOfSides = 100
var shape2 = shape
print(shape2.numberOfSides)
//100と出力される。
shape2.numberOfSides = 110
print(shape.numberOfSides)
//100と出力される。
print(shape2.numberOfSides)
//110と出力される。

shape2にはshapeのインスタンスと、そのプロパティにある値のコピーが渡されていることがわかります。
次に、参照型ですが、値型と異なり、参照型は変数や定数に代入されるときや、関数に渡されるときにコピーされません。
コピーではなく、同じインスタンスの参照が渡されます。
参照とは、他の場所にあるデータ(オブジェクト含む)を指している情報を含む小さなオブジェクトであり、それ自身の中に(指している)データ自体を含みません。
なお、関数も含めてクロージャも参照型です。
上記クラスを使った次の例を見てください。

var shape = Shape()
shape.numberOfSides = 100
var shape2 = shape
print(shape2.numberOfSides)
//100と出力される。
shape2.numberOfSides = 110
print(shape.numberOfSides)
//110と出力される。
print(shape2.numberOfSides)
//110と出力される。

shape2にはshapeの参照が渡されていることがわかります。
次に以下のコードを見てください。

struct Shape {
 var numberOfSides = 0
 func simpleDescription() -> String {
 return “A shape with \(numberOfSides) sides.”
 }
}
let shape = Shape()
shape.numberOfSides = 1
//shapeは定数なのでプロパティを変更することはできないというエラーが発生する

これは、構造体が値型であるため、構造体のインスタンスを定数shapeに代入すると、そのプロパティも固定されて変更できないということを示しています。
次の場合はいかがでしょうか。

class Shape {
 var numberOfSides = 0
 func simpleDescription() -> String {
 return “A shape with \(numberOfSides) sides.”
 }
}
let shape = Shape()
shape.numberOfSides = 1
//エラーなし

クラスの場合、定数shapeにはインスタンスの参照が入っているので、インスタンスは固定されますが、インスタンスのプロパティは固定されていないので、それを変更しても問題ありません。
しかし、次のように定数shapeに入っているインスタンスを変更しようとするとエラーが発生します。

let shape = Shape()
var shape2 = Shape()
shape = shape2

さて、この参照型と値型の違いにより、クラスのみ以下の方法を適用することができます。

  • 自動参照カウント
  • デイニシャライザ

クラスの場合、そのインスタンスが異なる対象から参照されるため自動参照カウント(ARC)の対象になります。
また、クラスの場合、自動参照カウント(ARC)の対象になるので、必要に応じて、インスタンスが割り当て解除される直前に呼び出されるデイニシャライザを利用することできます。
次に、クラスと構造体の意味の違いを考えてみましょう。
クラスは、本来、同じ属性(プロパティ)や振る舞い(メソッド)を持つ要素(インスタンス)を抽象化した集合(種類)を表していますが、
構造体の場合、複数のプロパティやメソッドを構造化した型ではありますが、クラスのような意味を持っていません。
この意味の違いにより、クラスのみ以下の方法を適用することができます。

  • 継承
  • 型キャスト

継承は集合に対する部分集合をつくる方法です。
型キャストは、そのインスタンスをクラス階層の異なるスーパークラスやサブクラスとして扱う方法です。
なお、次の方法は両方で使うことができます。

  • プロパティ
  • メソッド
  • サブスクリプト
  • イニシャライザ
  • エクステンション
  • プロトコル

クラスと構造体の使い分けについて

上述したように、
クラスは、意味的に、集合(種類)を表し、その要素である実体(インスタンス)は、それにアクセスするための参照を持ちますが、
構造体は、データや機能をまとめ構造化した型であり、そのインスタンスは、その型の値を表します。
Swiftの言語ガイドには、次の条件のうち 1 つ以上に当てはまるときには構造体を使い、それ以外はクラスを使うという指針が示されています。

  • 構造体の主な目的が、比較的単純なデータ値を持つことである
  • 構造体のインスタンスを代入する、または渡すときに、参照よりも値がコピーされることを期待するのが妥当である
  • 構造体に保持されたプロパティが値型で、参照よりもコピーされることが期待される
  • 構造体が、別の型からプロパティや振る舞いを継承する必要が無い

また、構造体の例として以下が示されています。

  • 幾何学的図形のサイズで、width プロパティと height プロパティがあり、共に Double 型
  • 一連の範囲を参照する手段で、start プロパティと length プロパティがあり、共に Int 型
  • 3D 座標系の点で、x, y, z プロパティがあり、すべて Double 型

ちなみに、Swift の基本的な型の整数、浮動小数点数、ブール、文字列、配列、辞書はすべて構造体として定義されています。
また、SwiftUIのViewも構造体として定義します。

エンティティと値オブジェクトによる使い分け

ソフトウェアの設計手法の一つにデータ駆動型設計(domain-driven design:DDD)があります。
ドメイン駆動設計ではドメインモデル(開発対象のモデル)を表現する要素を4つに分類していますが、その中にエンティティと値オブジェクト(Value Object)があり以下のような違いがあります。

  • エンティティ(参照オブジェクト)
    ドメインモデルを構成するオブジェクトで連続性と識別性によって定義される。

    • 同一性の判断
      識別子が同一であれば同一。
    • 可変性
      可変。生成されてから、時間と共に状態(属性値)が変化する。

    • 社員番号によって識別される社員オブジェクト。
  • 値オブジェクト
    ドメインモデルを構成するオブジェクトで事物の特性を記述する。特に識別する情報はなく、通例、読み出し専用のオブジェクトとして用いられる。

    • 同一性の判断
      保持する属性値が全て同一であれば同一。
    • 可変性
      不変。生成されたら、あとは破棄されるのみ。

    • 100円を表すオブジェクト。

この場合、エンティティ(参照オブジェクト)は参照型であるクラスで実装し、値オブジェクトは値型である構造体で実装すればよいかと思います。
つまり、実体(エンティティ)を表すオブジェクトを実装する場合はクラス、やり取りしたいデータのかたまりを表すオブジェクトを実装する場合は構造体という使い分けをすればよいのではないでしょうか。
以上、今回は、Swiftのクラスと構造体の違いについて解説しました。

-Swift
-, ,

執筆者:

関連記事

Swiftのメモリ管理【weakやunownedをわかりやすく解説】

Swiftのプログラムでweakやonwnedというキーワードをみたことはありませんか? これはSwiftが採用しているARCというメモリ管理の仕組みに関係しています。 今回は以下の観点でSwiftの …

Swiftの列挙型をわかりやすく解説

プログラムを作成するとき、曜日(月曜日、火曜日、水曜日、木曜日、金曜日、土曜日、日曜日)やカードのスート(スペード、クラブ、ダイヤ、ハード)のように決まった範囲の要素(種類)を持つ集合を扱う場合がある …

Swiftの関数型についてわかりやすく解説

Swift のすべての関数には型があり、関数のパラメータの型と戻り値の型で構成されます。 この型を Swift の他の型と同じように使うことができ、他の関数にパラメータとして関数を渡すことや、関数から …

Swiftのクロージャについてわかりやすく解説

Swiftのクロージャって何? いまひとつわからない、という方むけに、今回は、Swiftのクロージャについて以下の観点で丁寧に解説します。 Swiftのクロージャとは何か Swiftのクロージャ式 S …

Swiftの継承についてわかりやすく解説

クラスは、別のクラスからメソッド、プロパティ、および他の特徴を継承することができます。 あるクラスが別のクラスから継承するとき、継承するクラスをサブクラス、継承されるクラスをスーパークラスと呼びます。 …