LoginSignup
1
1

More than 3 years have passed since last update.

【Swift】型の構成要素〜イニシャライザ〜

Last updated at Posted at 2020-12-13

イニシャライザ

イニシャライザは、型のインスタンスを初期化します。

全てのプロパティは、インスタンス化の完了までに値が代入されていなければなりません。

なので、プロパティの宣言時に初期値を持たないプロパティは、
イニシャライザ内で初期化する必要があります。

レイジーストアドプロパティは、アクセスするまで生成されないので、
初期値を持っていない状態とは少し違います。

定義方法

イニシャライザは、initキーワードで宣言します。
定義内容としては、引数と初期化に関する処理を定義します。

イニシャライザの引数の文法は、関数の引数と同じです。


init(引数) {
   初期化処理
}

例として構造体Sampleを作成してみました。

init(a: String) { }の部分がイニシャライザになります。
String型の引数を一つ受け取っています。

self.a = aself aはプロパティaを指します。
右辺のaは引数名のaを指します。

この処理でプロパティaの中には引数の値であるhelloが格納されました。

プロパティbはゲッタなので、
sample.bで値を受け取っています。


struct Sample {
    let a: String
    var b: String {
        return "a = \(a)"
    }

    init(a: String) {
        self.a = a
    }
}

let sample = Sample(a: "hello")
print(sample.b)

実行結果
a = hello

失敗可能なイニシャライザ

イニシャライザは本来全てのプロパティを正しい型の値で初期化する役割を果たしていますが、
イニシャライザの引数によってはプロパティを初期化できないケースが出てきます。

初期化に失敗する可能性があるイニシャライザは
失敗可能イニシャライザとして表現でき、結果をOptional<Wrapped>型で返します。

失敗可能イニシャライザは、initキーワードに?を加えてinit?(引数)のように記述します。

初期化が失敗した場合はインスタンス化が行われないので、
プロパティを未初期化のままにすることが可能です。

失敗可能イニシャライザの例を書いてみました。

下に流れを順に書いています。


struct Student {
    let name: String
    let number: Int

    init?(info: [String: Any]) {
        guard let name = info["name"] as? String, let number = info["number"] as? Int else {
            return nil
        }

        self.name = name
        self.number = number
    }
}


let students: [[String: Any]] = [
    ["name": "Aihara", "number": 1],
    ["name": "Inoue", "number": 2],
    ["name": "Tanaka"],
    ["name": "Watanabe", "number": 40]
]

for student in students {
    if let item = Student(info: student) {
        print(item)
    } else {
        print("エラー:生徒情報\(student)からStudentを生成できませんでした。")
    }
}

実行結果
Student(name: "Aihara", number: 1)
Student(name: "Inoue", number: 2)
エラー生徒情報["name": "Tanaka"]からStudentを生成できませんでした
Student(name: "Watanabe", number: 40)

二次元配列の定義
let students: [[String: Any]] = [・・・]
[String: Any]型の辞書が格納されている二次元配列を定義しました。

for文
for student in students {・・・}
上で定義した二次元配列の中身の[String: Any]型の値をstudentに格納しfor文を回します。

for文の中のif文
if let item = Student(info: student) {・・・}
studentの中に入っている値を引数に指定し、構造体Studentをインスタンス化しています。
値がnilでない場合はitemに値を入れ、nilの場合はelse節に移動します。

イニシャライザ
init?(info: [String: Any]) {・・・}
インスタンス化される際に渡されている引数でイニシャライザを行います。

guard let name = info["name"] as? String
引数でもらった値は引数名infoからアクセスできます。

処理としては、info["name"]でキーをnameにしているので、
それに該当する値をas? Stringつまり、String型として取り出しています。

let number ・・・の方も同じ流れです。

両方とも値が存在した場合は、
self.name = nameself.number = numberが実行されそれぞれ値が入ります。

しかし、どちらかの値が存在しなかった場合はguard-let文の中に移動し、
return nilでnilが返されます。

といった処理の流れになります。
少しややこしいですが理解できましたか・・・?

説明が下手でしたらすみません。

初期化チェック

プロパティの初期化はコンパイラによってチェックされています。

一つでもプロパティが初期化されていない場合は、
型の整合性が取れなくなってしまうのでコンパイルエラーになってしまいます。

コンパイルエラーになる例は次のようなコードです。

エラーが起きる理由としては、
Sample型のインスタンス化時にプロパティnameの値がセットされておらず
Samole型のインスタンスが持つべき値が全て揃っていないためエラーになります。


struct Sample {
    var name: Int
    var message: String {
        return "hello \(name)"
    }

    init(name: String) {
        // コンパイルエラー
    }
}

エラー内容:Return from initializer without initializing all stored properties
和訳:保存されているすべてのプロパティを初期化せずに初期化子から戻る

イニシャライザ内で全てのプロパティに対して初期化処理を行えば、
型の整合性を保つことができるのでコンパイルエラーはなくなります。


struct Sample {
    var name: String
    var message: String {
        return "hello \(name)"
    }

    init(name: String) {
        self.name = ""
    }
}

また、プロパティの初期化は行っているつもりでも、
初期化せずにインスタンス化を終えてしまう条件が
一つでも存在する場合も残念ながらコンパイルできません。


struct Sample {
    var name: String
    var message: String {
        return "hello \(name)"
    }

    init(dictionary: [String: String]) {
        if let name = dictionary["name"] {
            self.name = name
        }
        // コンパイルエラー
    }
}

if let name = dictionary["name"]で、
値が存在すればプロパティnameに値を入れています。

しかし、値が存在しない場合の処理が何も書かれていないため、
プロパティnameには値が入らずに終了してしまします。

対処方法としては、else節で初期化の処理を記述する失敗可能イニシャライザにする
デフォルト値でプロパティを埋めるなどの選択指があります。


// else節に処理を記述する
struct ElseSample {
    var name: String
    var message: String {
        return "hello \(name)"
    }

    init(dictionary: [String: String]) {
        if let name = dictionary["name"] {
            self.name = name
        } else {
            self.name = "none"
        }
    }
}

// init?()を使い失敗可能イニシャライザを使用する
struct InitSample {
    var name: String
    var message: String {
        return "Hello \(name)"
    }

    init?(dictionary: [String: String]) {
        guard let name = dictionary["name"] else {
            return nil
        }
        self.name = name
    }
}

// デフォルト値でプロパティに値を入れる
struct DefaultSample {
    var name: String
    var message: String {
        return "Hello \(name)"
    }

    init(dictionary: [String: String]) {
        name = dictionary["name"] ?? "none"
    }
}

以上がイニシャライザの説明になります。

構造体を作る上でイニシャライザは大切な構成要素なので、
ぜひ覚えていろいろな場面で使ってみてください!

また、他の構成要素についても記事にしているのでぜひご覧ください!

【Swift】型の構成要素〜型の基本〜
【Swift】型の構成要素〜プロパティ前編〜
【Swift】型の構成要素〜メソッド〜
【Swift】型の構成要素〜サブスクリプト〜
【Swift】型の構成要素〜エクステンション〜
【Swift】型の構成要素〜型のネスト〜

最後までご覧いただきありがとうございました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1