Swiftの構造体の使い方

Swift2の構造体の文法をまとめました。

Swiftのクラスと構造体は機能的にとても似ています。
定数、変数、関数を同じ文法で作れます。

他の言語(Objective-C、C++など)ではクラスはオブジェクトとして知られている。
Swiftではクラスと構造体がより機能的に近しいので、「インスタンス」の用語を使ったほうが良い

クラスと構造体ができること

クラスと構造体がどちらもできるがいくつかあります。

  • プロパティを宣言
  • メソッドを宣言
  • 添字(サブスクリプト)を宣言
  • 初期化
  • 拡張
  • プロトコルの適用

Objective-Cだとメソッドの宣言などはできなかったですが、Swiftはかなり柔軟に構造体を使えるようになっています。

クラスのみができること

逆に、クラスだけができることは以下です。

  • 継承ができる
  • 型の変換(キャスト)ができる
  • デイニシャライザができる
  • 参照カウントでメモリを管理

ARCはクラスのみにある機能みたいですね。
あとでも出てきますが、構造体は値渡しで代入時に構造体インスタンスのコピーが渡されるのでARCは必要ないみたいです。

文法

構造体の宣言は'struct'を始めに書きます。

struct DisplaySize {
    var widthPoint = 0
    var heightPoint = 0
    var retina = 0
}

Swiftの命名規則
型はアッパーキャメルケースで記述が望ましいので、最初の単語を大文字から始めて、次単語の始めを大文字で表記します。
プロパティと関数はローワーキャメルケースでの記述が望ましいので、最初の単語は小文字から始めて、次の単語の始めを大文字で表記します。

宣言した構造体はインスタンス化して使用ができるようになります。

//構造体の初期化
var iPhone4s = DisplaySize()
iPhone4s.widthPoint = 320
iPhone4s.heightPoint = 480
iPhone4s.retina = 2

構造体は自動的にそれぞれのメンバーを用いた初期化が定義されています。

//構造体はメンバー全てを指定する初期化メソッドが予め作成されている。
let iPhone6sSize = DisplaySize(widthPoint: 375, heightPoint: 667, retina: 2)

プロパティへのアクセスは.(ドット)で実現できます。

let iPhone6sSize = DisplaySize(widthPoint: 375, heightPoint: 667, retina: 2)
var myDevice = IOSDevice(name: "iPhone6s", displaySize: iPhone6sSize)
print(myDevice.displaySize.widthPoint)//->375

構造体の代入は値渡し

構造体と列挙型の代入は値渡しです。
なので、コピー先のプロパティを変更してもコピー元のプロパティはもとの値のままです。

/*
構造体と列挙型は値渡し
*/
let iPhone6sSize = DisplaySize(widthPoint: 375, heightPoint: 667, retina: 2)
var iPhone6Size = iPhone6sSize
iPhone6Size.widthPoint = 480
print(iPhone6sSize.widthPoint)//->375
print(iPhone6Size.widthPoint)//->480

クラスは参照渡し

逆にクラスの変数を代入した時はその変数が示すインスタンスの参照が渡されます。
なので、コピー元、コピー先どちらかのプロパティを変更すれば片方もその変更が反映されてしまいます。

/*
クラスを定義する
*/
class IOSDevice {
    var name : String
    let displaySize : DisplaySize
    
    init(name : String, displaySize : DisplaySize){
        self.name = name
        self.displaySize = displaySize
    }
}

var myDevice = IOSDevice(name: "iPhone6s", displaySize: iPhone6sSize)
var yourDeviece = myDevice
yourDeviece.name = "iPhone6"//myDeviceも変更される!!

print(myDevice.name)//->iPhone6
print(yourDeviece.name)//->iPhone6

構造体のメソッド

Objective-Cでは構造体にはメソッドは作れませんでしたが、Swiftではメソッドを作れるようになりました。

メソッドの書き方はクラスの時と一緒です。
自分のプロパティを変更するメソッドを作る場合はmutatingの修飾子をつけます。

/*
構造体のメソッド
構造体のメンバーを変更する場合はmutatingをつける
*/

struct Menu {
    var recommendedMenu = "シェフのおすすめリゾット"
    var eatMenu = "ペペロンチーノ"
    let drinkMenu = "赤ワイン"
    func getAllMenu () -> [String]{
        return [self.recommendedMenu, self.eatMenu, self.drinkMenu]
    }
     mutating func changeRecommendMenu (menu : String){
        self.recommendedMenu = menu
        self.eatMenu = menu
    }
}

var sameRestaurantMenu = Menu()
sameRestaurantMenu.changeRecommendMenu("季節野菜のスープパスタ")
print(sameRestaurantMenu.recommendedMenu)

上記の例はインスタンスメソッドですが、クラスのクラスメソッドの様に、インスタンスしなくても使えるメソッドも宣言できます。
Swiftではタイプメソッドというそうです。
staticの修飾子をつけるとタイプメソッドとして宣言できます。


/*
タイプメソッド
構造体のインスタンスを作らなくても実行できる
*/
struct DrinkMenu {
    var beers : Array<String>? = ["アサヒスーパードライ"]
    var menuAmount : Int?
    
    mutating func sumMenu (){
        self.menuAmount = beers?.count
    }
    
    init(beer : String){
        beers?.append(beer)
        menuAmount = DrinkMenu.sumMenuNumber(beers!)
        //self.menuAmount = 5
    }
    
    static func sumMenuNumber (menus : Array<String>) ->Int{
        return menus.count
    }
}
var someDringMenu = DrinkMenu.init(beer: "バドワイザー")
print(someDringMenu.menuAmount!)

構造体の使いドコロ

  • 構造体の目的は比較的単純なデータをまとめること
  • 値代入の際に値渡しを用いたほうが合理的な場合
  • プロパティで参照よりもコピーされるべきもの
  • 継承をする必要がないとき

例:
四角形の幅と高さ、ある範囲の初期位置と長さ、3Dの軸X,Y,Z

String、Array、Dictionary

SwiftのString、Array、Dictionaryは構造体で定義されている
なので、値渡しで代入される

FoundationのNSString、NSArray、NSDictionaryはクラスで定義されている
なので参照渡しで代入される


参考

The Swift Programming Language (Swift 2.1)
https://itunes.apple.com/jp/book/swift-programming-language/id881256329?mt=11

Swift言語を学ぶ 構造体
http://tea-leaves.jp/swift/content/構造体

Swiftのクラスと構造体の使い分けについてのメモ
http://qiita.com/mini_house/items/22b7b88794b9f8a32381

詳解 Swift 改訂版
http://www.amazon.co.jp/dp/4797386258/ref=pd_lpo_sbs_dp_ss_1?pf_rd_p=187205609&pf_rd_s=lpo-top-stripe&pf_rd_t=201&pf_rd_i=4797380497&pf_rd_m=AN1VRQENFRJN5&pf_rd_r=1M2BCJAFVSPNDEFS64F9