これはstruct(構造体)かclass(クラス)どちらで宣言すべきか。慣れないうちは悩むことがあると思います。どちらにするのが良いかを考える基準について説明したいと思います。

参考: The Swift Programming Language: Classes and Structures

structとclassの違い

まずはstructとclassの違いを理解しておきましょう。

  • structは値渡し、classは参照渡し
  • structは継承できない、classは継承できる
    • ただしstructもextensionでメソッド追加はできる
  • structはselfのプロパティを変更するメソッドはmutatingキーワードが必要になる

structの方が望ましいもの

  • シンプルなデータ構造である
  • プロパティも値渡しの型である、つまりstructである
    • Int、Double、String、Dictionary、Arrayなども実体はstructです。だからこそ値渡しなわけです
  • メソッドの引数に渡す時、IntやDoubleを渡すときのように自身の更新を目的としない

SDKでstructで宣言されているものを参考にするとよいでしょう。

CGPointはstructで宣言されています。

struct CGPoint {
    var x: CGFloat
    var y: CGFloat
    init() {
    }
    init(x: CGFloat, y: CGFloat) {
        self.x = x
        self.y = y
    }
}

CGPointは先程のstructであるほうが望ましい基準に当てはまると思います。

  • xとyだけのシンプルなデータ構造である
  • xもyも値渡し型であるCGFloat
  • 引数で渡す時、自身の更新を目的としない
  • xとyをばらばらで各所で宣言するよりはCGPointとしてまとまっている方がわかりやすく、コードを書く量が減る
    • CGPointとしてまとまっている方が、CGPointEqualToPointなどのユーティリティ関数を用意しやすい

最後のユーティリティ関数ですが、自分でstructを作る場合、関数よりもstructにメソッドを追加したほうが良いです。CGRectはそのようになっています。

structにしない方が良いもの

structにしない方が良いケースを知っておくことで、どちらにするか考えやすくなるかもしれません。

サイズが大きくなるもの

値渡しであることは、コピーが作られるということで、プロパティの数が多いとメモリ消費も大きくなります。巨大なデータになりうるものはstructにしないほうがよいでしょう。

プロパティにclassのオブジェクト(参照渡し)のものが含まれるもの

structは値渡しであるはずなのに、そのプロパティに参照渡しのものが含まれていると混乱の元になります。以下はその例です。

struct MyStruct {
    // 参照渡しのクラスオブジェクト
    var data: NSMutableData

    init(data: NSMutableData) {
        self.data = data
    }
}

// 文字列をNSMutableDataにする
var data1 = NSMutableData(data: NSString(string: "Data1").dataUsingEncoding(NSUTF8StringEncoding)!)
var data2 = NSMutableData(data: NSString(string: "Data2").dataUsingEncoding(NSUTF8StringEncoding)!)

// MyStructをオブジェクト化
var val1 = MyStruct(data: data1)
// 値渡しであるためval2はval1のコピー
// しかしval1.dataもval2.dataも同じオブジェクト(data1 = "Data1")を参照する
var val2 = val1
// val2.dataを更新するとval1.dataも更新される
val2.data.setData(data2)

var str1 = NSString(data: val1.data, encoding: NSUTF8StringEncoding)!
var str2 = NSString(data: val2.data, encoding: NSUTF8StringEncoding)!
// str1 => "Data2"
// str2 => "Data2"

このようなことを避けるためにもstructにはclassオブジェクトを含まないほうが望ましいでしょう。

基本はclassにしましょう

慣れないうちは迷うことが多いかもしれませんが、迷ったらclassで宣言する方が無難だと思います。その中で上記のような条件に当てはまり、structのほうがふさわしいなら、後で変更するでも良いと思います。もちろん、参照渡しだったものが値渡しになることでエンバグする可能性もあるので、そこは注意が必要です。