構造体

カプセル化を実現する方法としてSwiftには、クラスの他に構造体(struct)が用意されています。但し、クラスと構造体には次のような違いがあります。

  • 構造体では継承は利用できません。
  • 構造体にデイニシャライザは定義できません。
  • クラスのインスタンスを別の変数に代入すると参照が渡されます。インスタンスの参照数は参照カウントで管理されます。対して構造体を別の変数に代入すると新たなコピーが生成されます。構造体の参照先は常に1つなので参照カウントは使用されません。

構造体の書き方はクラスとよく似ています。


/* 著者 */
struct Author {
    var name: String = "不明"
    var birthday: NSDate?   // NSDateはCocoaフレームワークの日付型
}

let author = Author()

イニシャライザを使ってメンバ変数を初期化することもできます。


/* 著者 */
struct Author {
    var name: String
    var birthday: NSDate?
    init() {
        name = "不明"
    }
}

let author = Author()

また、構造体はメンバ変数を引数にもつイニシャライザが自動的に生成されるため、イニシャライザを自前で用意しなくても次のように初期化することができます。


/* 著者 */
struct Author {
    var name: String
    var birthday: NSDate?
}

let formatter = NSDateFormatter()
formatter.dateFormat = "Y/M/d"
let author = Author(name: "夏目漱石", birthday: formatter.dateFromString("1867/2/9"))

プロパティへのアクセスはクラスの場合と同様、.(ドット)を使います。


var author = Author(name: "夏目漱石", birthday: formatter.dateFromString("1867/2/9"))
print("著者:" + author.name)   // 著者:夏目漱石

author.name = "森鴎外"
author.birthday = formatter.dateFromString("1862/2/17")
print("著者:" + author.name)   // 著者:森鴎外

構造体のメンバに構造体が含まれる場合は次の様に.(ドット)で繋いでアクセスします。


/* 著者 */
struct Author {
    var name: String = "不明"
    var birthday: NSDate?
}

/* 書籍 */
struct Book {
    var title: String
    var author: Author
}

let formatter = NSDateFormatter()
formatter.dateFormat = "Y/M/d"

let book = Book(
    title: "吾輩は猫である",
    author: Author(name: "夏目漱石", birthday: formatter.dateFromString("1867/2/9"))
)

print("書籍名:\(book.title), 著者:\(book.author.name)")    // 書籍名:吾輩は猫である, 著者:夏目漱石

定数として宣言した構造体のプロパティを変更することはできません。


let author = Author(name: "夏目漱石", birthday: formatter.dateFromString("1867/2/9"))
author.name = "森鴎外" // エラー(変更できない)

構造体は変数へ代入される時にプロパティも含めてコピーされます。別の変数に代入した構造体のプロパティを変更しても、元の構造体のプロパティの値は変わりません。
参照が代入され同じインスタンスを複数の変数で共有するクラスの場合とは動作が異なります。


let author = Author(name: "夏目漱石", birthday: formatter.dateFromString("1867/2/9"))
var book1 = Book(title: "坊ちゃん", author: author)
var book2 = book1
book2.title = "吾輩は猫である"

print(book1.title)    // 坊ちゃん
print(book2.title)    // 我が輩は猫である

SwiftのString、Array、Dictionaryは、構造体を使って実装されています。つまり、これらを他の変数に代入したり、関数の引数として渡す時には、値がコピーされます。クラスとして実装されているCocoaのNSString、NSArray、NSDictionaryとはこの辺の動作が異なっています。
但し、Swiftのこれらのインスタンスのコピーも、実際に必要になった時点(コピー先のプロパティが実際に変更された時等)でコビーされるようにコンパイラが最適化してくれるので、オーバーヘッドについて過剰に心配する必要はありません。