【Swift】構造体

構造体とは値型と言われるもののひとつで変数や定数、関数をまとめて保持できるものです。
複数の変数や定数、関数をまとめて1つのオブジェクトとして保持し、汎用的に使用したい場合に使います。
作った構造体はインスタンス化することで使用することができるようになります。

前回は 関数 について書きました。

今回は 構造体 について書いていこうと思います。

構造体とは

構造体とは値型と言われるもののひとつで変数や定数、関数をまとめて保持できるものです。
複数の変数や定数、関数をまとめてひとつのオブジェクトとして保持し汎用的に使用したい場合に使います。
作った構造体は インスタンス化 することで使用することができるようになります。

構造体の概要

  • 複数のインスタンス間で値を共有せず、各インスタンスが値を保持する
  • 構造体の中に宣言された変数や定数を プロパティ という
  • 構造体の中に定義された関数を メソッド という
  • 構造体をインスタンス化する時に初めて値を決める場合が多いので、構造体のプロパティ定義時には初期値を決める必要はない(プロパティの宣言のみで良い)

インスタンス化とは

インスタンスとは 実体 という意味で、定義した構造体や class を元にプロパティに具体的な値を代入してインスタンス(実体)を作ります。これをインスタンス化と言います。
構造体や class をインスタンス化する時は、インスタンス化する時点でプロパティの初期値が決まってなければならないので、プロパティに初期値を入れるためには、 イニシャライザ で初期化することが必要となります。

イニシャライザとは

イニシャライザとは、構造体や class のプロパティを初期化( initialize )する特殊なメソッドのことです。他の言語でいうコンストラクタと同様。
型の整合性を保つためだったり、構造体や class のプロパティに値が無いままインスタンス化しようとしてエラーになるのを防ぐために必要となります。

※構造体と似ているものとして class が挙げられると思います。
書き方や見た目は似ていますが、
違いは構造体は 値型 であることに対して class は 参照型 であること、また、
値型 はインスタンス化する時、データ自体がコピーされて渡されるのに対して、
参照型 はインスタンス化する時、コピーではなく既存のデータが格納されている場所を参照するという所に違いがあります。
class についてはまた今度の記事で書こうと思います。

構造体とイニシャライザの書式

構造体の書式

struct 構造体名{
 プロパティ宣言
 イニシャライザ定義
 メソッド定義
 }

構造体は頭に struct キーワードを書い後に、構造体名を書きます。
中括弧 { } の中にプロパティイニシャライザメソッドを定義していきます。

イニシャライザの書式

init (引数名: 型名, ...){
     self.プロパティ名 = 値(値は引数のものでも良いし、引数同士を計算したものでも良い)
     (*全てのプロパティを初期化する)
     }
 }

イニシャライザは頭に init キーワードを付けます。
その後に ( ) を書いて中に 引数名 : 型名 をカンマ , で区切って必要な分書きます。
構造体をインスタンス化する時に、ここで書いた引数に初期値を入れることでインスタンス化することが出来ます。
中括弧 { } の中に初期化する処理を書いていきます。

構造体の定義例

struct Status{
    
    //プロパティ
    let name: String
    var hp: Int
    var mp: Int
    var attack: Int
    
    //イニシャライザ
    init(name:String, HP:Int, MP:Int, attack:Int) {
        self.name = name
        self.hp = HP
        self.mp = MP
        self.attack = attack
    }
    
    //メソッド
    func currentStatus() {
        print("\(name)のステータス")
        print("HP: \(hp)")
        print("MP: \(mp)")
        print("ちから: \(attack)")
    }

}

上のコードについて解説↓

構造体名

struct Status{
}

構造体の名前は Swift で推奨されているアッパーキャメルケースで書きます。
命名規則についてはこちらの記事で紹介しています。

プロパティ

struct Status{
    
    //プロパティ
    let name: String
    var hp: Int
    var mp: Int
    var attack: Int

}

構造体 Status には定数 1 つと変数 3 つの、計 4 つのプロパティを宣言していますが初期値は定義していません。後でインスタンス化するときに初期値を入れたいので宣言のみにしています。

イニシャライザ

struct Status{
    
    //プロパティ
    let name: String
    var hp: Int
    var mp: Int
    var attack: Int
    
    //イニシャライザ
    init(name:String, HP:Int, MP:Int, attack:Int) {
        self.name = name
        self.hp = HP
        self.mp = MP
        self.attack = attack
    }

}

( 引数名 : 型名 ) のところはプロパティの数だけ必要になる( プロパティ全ての初期化が完了しないとインスタンス化出来ません。)ので、引数を 4 つ書いています。
中括弧 { } の中に、引数から取った値を各プロパティに代入するという処理( 紐付け )を書いています。
self キーワードは構造体自身の事を指していて、まだインスタンス化されていないためプロパティを指す事ができません。 self. とする事で、自身のプロパティである事を明示しています。
※ 明らかに自身のプロパティであることが分かる状況であれば、self. は省略出来ます。

メンバーワイズイニシャライザ

メンバーワイズイニシャライザとはデフォルトで用意されているイニシャライザで、
単純または簡単なプロパティの初期化であれば、init関数 を書かなくても内部で暗黙的にイニシャライズされる機能です。
こっちの方が楽ですが、今回は init関数 を使ってイニシャライズして進めていきます。

メソッド

struct Status{
    
    //プロパティ
    let name: String
    var hp: Int
    var mp: Int
    var attack: Int
    
    //イニシャライザ
    init(name:String, HP:Int, MP:Int, attack:Int) {
        self.name = name
        self.hp = HP
        self.mp = MP
        self.attack = attack
    }
    
    //メソッド
    func currentStatus() {
        print("\(name)のステータス")
        print("HP: \(hp)")
        print("MP: \(mp)")
        print("ちから: \(attack)")
    }

}

インスタンス化したときのプロパティの初期値を確認したいので、現在の値を print() するメソッドです。
エスケープシーケンスを使って文字列の中に変数や定数を埋め込んでいます。

関数 についてはこちらの記事で紹介しています。

エスケープシーケンス についてはこちらの記事で紹介しています。

structコードimg

インスタンス化の書式

引数がない構造体の場合の書式

//構造体の名前の後に () を書きます

構造体名()

イニシャライザで引数を指定している場合の書式

//構造体の名前の後に () を書いて、中の引数に初期値を入力します

構造体名(イニシャライザで指定した引数, ...)

構造体をインスタンス化する例

struct Status{
    
    //プロパティ
    let name: String
    var hp: Int
    var mp: Int
    var attack: Int
    
    //イニシャライザ
    init(name:String, HP:Int, MP:Int, attack:Int) {
        self.name = name
        self.hp = HP
        self.mp = MP
        self.attack = attack
    }
    
    //メソッド
    func currentStatus() {
        print("\(name)のステータス")
        print("HP: \(hp)")
        print("MP: \(mp)")
        print("ちから: \(attack)")
    }

}

//構造体Statusをインスタンス化
var status = Status(name: "ゲレゲレ", HP: 100, MP: 30, attack: 90)

構造体のイニシャライザで設定した引数にそれぞれ値を入力して、変数 status に代入しています。
これで構造体 Status をインスタンス化することが出来たので、この 変数 status が構造体 Status のインスタンスとして使えるようになりました。

次は引数に入れた値がきちんと入っているか、インスタンスにアクセスして構造体の中で定義したメソッド currentStatus を呼んで実行してみます。

インスタンスにアクセスしてメソッドを呼ぶ方法

メソッドにアクセスするときの書式

//メソッドに引数がある場合
インスタンス名.メソッド名(引数名:値, ...)

//メソッドに引数がない場合
インスタンス名.メソッド名()

メソッドに引数がある場合はメソッド名の後に ( ) を書いて中に引数を入力。
インスタンス名 . メソッド名 ( 引数名 : 値 )

引数がないメソッドの場合はメソッド名の後に ( ) だけ書きます。
インスタンス名 . メソッド名 ( )

メソッドにアクセス

~ ~ ~
//構造体Statusをインスタンス化
var status = Status(name: "ゲレゲレ", HP: 100, MP: 30, attack: 90)

//メソッドにアクセス
status.currentStatus()

//実行結果
/*
ゲレゲレのステータス
HP: 100
MP: 30
ちから: 90
*/

メソッドを実行すると、インスタンス化時に引数に入れた値がきちんと構造体の各プロパティに代入されています。
プロパティである変数や定数にアクセスしたい場合もメソッド同様、
インスタンス名 . プロパティ名 とすることでアクセスすることが出来ます。

プロパティの値を変更する

プロパティの値を変更する関数を作って実行し、再度値の確認をしてみたいと思います。

~ ~ ~
//構造体Statusをインスタンス化
var status = Status(name: "ゲレゲレ", HP: 100, MP: 30, attack: 90)

//メソッドにアクセス
status.currentStatus()

//実行結果
/*
ゲレゲレのステータス
HP: 100
MP: 30
ちから: 90
*/

//プロパティの値を変更(更新)する関数
func levelUp(value: Int){
    status.hp += value
    status.mp += value
    status.attack += value
    
    var phrase = "\n\(status.name)はレベルが \(value) 上がった!\nHPが \(value) 上がった!\nMPが \(value) 上がった!\nちからが \(value) 上がった!\n"
    print(phrase)
}

引数を取る関数 levelUp を作ってみました。

処理の内容は、
関数で取った引数の値を、構造体の各プロパティに足します。その後、
引数の値を文字列に埋め込んで、変数 phrase に代入し print する、という処理にしています。

複合代入演算子 についてはこちらで紹介しています。

関数を実行してプロパティの値を変更してみます。

~ ~ ~
//インスタンス化
var status = Status(name: "ゲレゲレ", HP: 100, MP: 30, attack: 90)

//メソッドにアクセス
status.currentStatus()

//実行結果
/*
ゲレゲレのステータス
HP: 100
MP: 30
ちから: 90
*/

//プロパティの値を変更(更新)する関数
func levelUp(value: Int){
    status.hp += value
    status.mp += value
    status.attack += value
    
    var phrase = "\n\(status.name)はレベルが \(value) 上がった!\nHPが \(value) 上がった!\nMPが \(value) 上がった!\nちからが \(value) 上がった!\n"
    print(phrase)
}

//関数levelUpの引数に値を入力して実行
levelUp(value: 5)

//実行結果
/*
ゲレゲレはレベルが 5 上がった!
HPが 5 上がった!
MPが 5 上がった!
ちからが 5 上がった!
*/

上の例では関数の引数に 5 を入力して実行しています。
関数が実行されて、コンソールに実行結果が出力されたと思います。

最後に構造体のプロパティの値に変更が反映されているか、もう一度構造体のメソッドを呼んでみます。

~ ~ ~
//インスタンス化
var status = Status(name: "ゲレゲレ", HP: 100, MP: 30, attack: 90)

//メソッドにアクセス
status.currentStatus()

//実行結果
/*
ゲレゲレのステータス
HP: 100
MP: 30
ちから: 90
*/

//プロパティの値を変更(更新)する関数
func levelUp(value: Int){
    status.hp += value
    status.mp += value
    status.attack += value
    
    var phrase = "\n\(status.name)はレベルが \(value) 上がった!\nHPが \(value) 上がった!\nMPが \(value) 上がった!\nちからが \(value) 上がった!\n"
    print(phrase)
}

//関数levelUpの引数に値を入力して実行
levelUp(value: 5)

//実行結果
/*
ゲレゲレはレベルが 5 上がった!
HPが 5 上がった!
MPが 5 上がった!
ちからが 5 上がった!
*/

//プロパティに反映されているか確認
status.currentStatus()

//実行結果
/*
ゲレゲレのステータス
HP: 105
MP: 35
ちから: 95
*/

それぞれのプロパティに 5 ずつ足されています。
これで変更できました。

全コード

struct Status{
    
    //プロパティ
    let name: String
    var hp: Int
    var mp: Int
    var attack: Int
    
    //イニシャライザ
    init(name:String, HP:Int, MP:Int, attack:Int) {
        self.name = name
        self.hp = HP
        self.mp = MP
        self.attack = attack
    }
    
    //メソッド
    func currentStatus(){
        print("\(name)のステータス")
        print("HP: \(hp)")
        print("MP: \(mp)")
        print("ちから: \(attack)")
    }
}

//インスタンス化
var status = Status(name: "ゲレゲレ", HP: 100, MP: 30, attack: 90)

//メソッドにアクセスする
status.currentStatus()

//プロパティの値を変更(更新)する関数
func levelUp(value: Int){
    status.hp += value
    status.mp += value
    status.attack += value
    
    var phrase = "\n\(status.name)はレベルが \(value) 上がった!\nHPが \(value) 上がった!\nMPが \(value) 上がった!\nちからが \(value) 上がった!\n"
    print(phrase)
}

//関数levelUpの引数に値を入力して実行
levelUp(value: 5)

//プロパティに反映されているか確認
status.currentStatus()

//実行結果
/*
 ゲレゲレのステータス
 HP: 100
 MP: 30
 ちから: 90

 ゲレゲレはレベルが 5 上がった!
 HPが 5 上がった!
 MPが 5 上がった!
 ちからが 5 上がった!

 ゲレゲレのステータス
 HP: 105
 MP: 35
 ちから: 95
 */

お疲れ様でした。
以上 構造体 についてでした。

次回は 三項演算子 について書いていこうと思います。

実行環境

version
Xcode 14.2 (14C18)
Swift 5.2.4

公式ドキュメント

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/