読み込み中...

Swift初期化処理のたった17選!超わかりやすい解説と実例

Swift言語のロゴとコードスニペットを組み合わせたイメージ、初期化処理の詳細解説 Swift
この記事は約25分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Swiftは、Appleが開発したプログラミング言語であり、iOSやmacOSなどのAppleプラットフォームでのアプリ開発に広く用いられています。

この言語は安全性、パフォーマンス、そしてモダンなプログラミングパターンに焦点を当てて設計されており、多くの開発者から高い評価を受けています。

Swiftでの開発をスムーズに進めるためには、初期化処理の理解が不可欠です。

初期化処理は、オブジェクトが作成される際にそのオブジェクトの初期状態を設定するための処理を指します。

Swiftにおける初期化処理は、その他の多くのプログラミング言語といくつかの特徴的な違いがあります。

この記事では、Swiftでの初期化処理の基本から応用、そして注意点やカスタマイズ方法まで、具体的なサンプルコードと詳細な説明を交えてご紹介します。

●Swiftとは

Swiftは、Objective-Cに代わる新しいAppleのプログラミング言語として2014年に発表されました。

特に型安全性やパフォーマンス、読みやすさに重点を置いており、初心者からプロの開発者まで幅広く利用されています。

また、Swiftはオープンソースとして公開されており、コミュニティの貢献を受け入れながら進化しています。

●Swiftの初期化処理とは

初期化とは、インスタンスの生成時にそのインスタンスのプロパティや状態を適切な初期値に設定することを指します。

例えば、あるクラスが「年齢」というプロパティを持っている場合、インスタンスが生成される際に「年齢」に初期値として「0」を設定することが考えられます。

Swiftでは、初期化を行うための特別なメソッドが用意されています。

これを「イニシャライザ」と呼びます。イニシャライザは、インスタンスの生成と同時に自動的に呼び出され、インスタンスの初期設定を行います。

Swiftの初期化の特徴として、すべてのプロパティが適切な値に初期化されることが保証されています。

これにより、未初期化のプロパティを持つインスタンスが生成されることがなく、バグの発生を防ぐことができます。

○初期化の基本概念

Swiftの初期化処理は、主に次の3つのステップからなります。

  1. プロパティの初期値の設定
  2. イニシャライザの定義
  3. インスタンスの生成と初期化

1つ目のステップでは、クラスや構造体が持つプロパティにデフォルトの初期値を設定します。

この初期値は、イニシャライザ内で明示的に値が設定されない場合に使用されます。

2つ目のステップでは、イニシャライザを定義します。

イニシャライザは、インスタンスの生成時に自動的に呼び出されるメソッドであり、ここでプロパティの初期化やその他の初期設定を行います。

3つ目のステップでは、新しいインスタンスを生成し、イニシャライザを通じて初期化を行います。

この際、イニシャライザに引数を渡して、動的な初期設定を行うことも可能です。

●Swiftの初期化処理の使い方

Swiftの初期化とは、新しいインスタンスを使用する前にそのインスタンスのすべてのプロパティが正しい初期値に設定されていることを確保するプロセスのことです。

初期化はクラス、構造体、列挙体で行うことができます。

Swiftの初期化処理は、他のプログラム言語と比べても非常に安全性を重視しています。

そして、不正な状態のインスタンスが生成されることを防ぐための独自の機能が多く組み込まれています。

○サンプルコード1:デフォルト初期化

Swiftにおいて、全てのプロパティにデフォルト値が設定されている場合、そのクラスや構造体はデフォルトの初期化子を自動的に受け取ります。

このことをデフォルト初期化と呼びます。

class SimpleClass {
    var name: String = ""
    var age: Int = 0
}

let example = SimpleClass()

このコードではSimpleClassというクラスを作成しています。

このクラスではnameageという二つのプロパティを持っており、それぞれデフォルト値として空の文字列と0を持っています。

この例ではSimpleClassは自動的にデフォルトの初期化子を持っていますので、SimpleClass()という形で新しいインスタンスを作成することができます。

実際にこのコードを実行すると、exampleという名前のSimpleClassのインスタンスが作成され、そのnameageプロパティはそれぞれ空の文字列と0に初期化されることになります。

○サンプルコード2:指定初期化

Swiftでは、初期化時にプロパティに特定の値を設定することができます。

これを指定初期化と呼びます。

class Person {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let john = Person(name: "John", age: 25)

このコードではPersonというクラスを作成しています。

このクラスはnameageという二つのプロパティを持っており、それぞれ初期値が設定されていません。

その代わりに、指定初期化子initを使ってプロパティの値を初期化しています。

この例ではPerson(name: "John", age: 25)という形で新しいインスタンスを作成し、そのnameageプロパティを指定の値で初期化しています。

実際にこのコードを実行すると、johnという名前のPersonのインスタンスが作成され、そのnameは”John”、ageは25に初期化されることになります。

○サンプルコード3:便利な初期化の省略形

Swiftには、プロパティの初期値設定をより簡潔に書くための、便利な初期化の省略形が存在します。

struct Rectangle {
    var width: Double
    var height: Double

    init(_ width: Double, _ height: Double) {
        self.width = width
        self.height = height
    }
}

let rect = Rectangle(100.0, 200.0)

このコードではRectangleという構造体を作成しています。

この構造体はwidthheightという二つのプロパティを持っています。

そして、initメソッド内で引数名を省略していますので、Rectangle(100.0, 200.0)のように簡潔に新しいインスタンスを作成することができます。

このコードを実行すると、rectという名前のRectangleのインスタンスが作成され、そのwidthは100.0、heightは200.0に初期化されることになります。

●初期化時のプロパティ設定

初期化時には、オブジェクトのプロパティを設定することが一般的です。

プロパティとは、オブジェクトが持つ変数や定数のことを指し、これらの値を適切に設定することで、オブジェクトの動作を制御します。

○サンプルコード4:遅延プロパティの初期化

遅延プロパティは、初回アクセス時にその値が初めて計算されるプロパティのことを指します。

これは、そのプロパティの初期化にコストがかかる場合や、初期化時点ではその値を決定するのが難しい場合に有効です。

class SampleClass {
    // 遅延プロパティの宣言
    lazy var expensiveProperty: String = {
        print("初回アクセス")
        return "遅延初期化されたプロパティ"
    }()
}

let instance = SampleClass()
print(instance.expensiveProperty)  // 初回アクセス時に初期化される

このコードでは、SampleClassというクラス内に、expensivePropertyという遅延プロパティを定義しています。

この例では、expensivePropertyが初めてアクセスされる際に「初回アクセス」と表示され、遅延初期化された値が返されます。

このコードを実行すると、初回アクセス時に「初回アクセス」というメッセージが表示され、その後に「遅延初期化されたプロパティ」という文字列が返されます。

○サンプルコード5:計算型プロパティの初期化

計算型プロパティは、実際には値を保持しないプロパティのことを指し、その値は都度計算されて返されます。

これは、関連する他のプロパティの値に基づいて動的に値を返したい場合などに有効です。

struct Rectangle {
    var width: Double
    var height: Double

    // 計算型プロパティの定義
    var area: Double {
        return width * height
    }
}

let rect = Rectangle(width: 10, height: 5)
print(rect.area)  // 50.0

このコードでは、Rectangleという構造体に、widthheightという2つのプロパティと、それに基づいて面積を計算して返すareaという計算型プロパティを定義しています。

この例では、widthが10、heightが5の場合、areaは50.0として計算されます。

このコードを実行すると、面積として50.0が得られることが確認できます。

●初期化の際の引数の受け取り方

Swiftの初期化処理には、引数を受け取ることも可能です。

引数を受け取ることで、オブジェクトを作成する際に特定の値で初期化したいときや、初期化の処理を複数のパターンで行いたい場合に非常に便利です。

ここでは、引数を持つ初期化と、初期化のオーバーロードについて解説します。

○サンプルコード6:引数を持つ初期化

Swiftのクラスや構造体において、初期化の際に引数を受け取る方法を見てみましょう。

// 人を表すクラス
class Person {
    var name: String
    var age: Int

    // 引数を持つ初期化メソッド
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

// インスタンス化時に引数を指定して初期化
let tanaka = Person(name: "田中", age: 30)

このコードでは、Personというクラスを作成しています。

nameageという2つのプロパティを持ち、初期化メソッドinitで引数を受け取るように設計されています。

具体的には、nameageを受け取り、それらの値でプロパティを初期化しています。

この例では、tanakaというインスタンスを生成する際に、nameに”田中”、ageに30という値を指定して初期化しています。

実行すると、tanakaというPersonクラスのインスタンスが作成され、そのnameプロパティには”田中”、ageプロパティには30という値がセットされることになります。

○サンプルコード7:初期化のオーバーロード

Swiftでは、同じ名前のメソッドや関数を異なる引数で複数定義することができます。

これをオーバーロードと呼びます。

初期化処理においても、異なる引数の組み合わせで複数の初期化メソッドを定義することができます。

// 人を表すクラス
class Person {
    var name: String
    var age: Int?

    // 引数を持つ初期化メソッド1
    init(name: String) {
        self.name = name
    }

    // 引数を持つ初期化メソッド2
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

// インスタンス化
let suzuki = Person(name: "鈴木")
let sato = Person(name: "佐藤", age: 25)

このコードでは、Personクラスに2つの初期化メソッドをオーバーロードしています。

一つはnameのみを引数として受け取り、もう一つはnameageの2つを引数として受け取ります。

この例では、suzukiというインスタンスはnameのみを指定して初期化され、satoというインスタンスはnameageを指定して初期化されます。

実行すると、suzukinameプロパティには”鈴木”が、satonameプロパティには”佐藤”、ageプロパティには25がセットされることになります。

●継承と初期化

Swiftのクラスの特性の1つとして、継承があります。

継承を利用することで、既存のクラスの特性や機能を新しいクラスに引き継ぐことができます。

しかし、継承と初期化の関連性は少々複雑です。

ここでは、継承と初期化の関連性について詳しく探っていきます。

○サンプルコード8:サブクラスの初期化

このコードでは、親クラスからサブクラスへの初期化の流れを表しています。

この例では、親クラスであるAnimalクラスから継承されたDogクラスの初期化を行っています。

class Animal {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class Dog: Animal {
    var breed: String
    init(name: String, breed: String) {
        self.breed = breed
        super.init(name: name)
    }
}

let myDog = Dog(name: "Buddy", breed: "Golden Retriever")

上記のコードでは、DogクラスはAnimalクラスを継承しています。

Dogクラスの初期化時には、まず自身のプロパティであるbreedを初期化した後、親クラスであるAnimalのinitメソッドをsuper.init(name: name)という形で呼び出しています。

このコードを実行すると、myDogという名前のGolden Retriever種のDogオブジェクトが作成されます。

○サンプルコード9:オーバーライドと初期化

このコードでは、親クラスの初期化メソッドをオーバーライドする例を表しています。

この例では、Vehicleクラスを継承したCarクラスが親クラスのinitメソッドをオーバーライドしています。

class Vehicle {
    var numberOfWheels: Int
    init(wheels: Int) {
        self.numberOfWheels = wheels
    }
}

class Car: Vehicle {
    override init(wheels: Int = 4) {
        super.init(wheels: wheels)
    }
}

let sedan = Car()

Carクラスでは、Vehicleのinitメソッドをオーバーライドしていますが、デフォルトの車輪数として4を設定しています。

このため、Car()と初期化するだけで、車輪数が4のCarオブジェクトが生成されます。

このコードを実行すると、sedanという名前の車輪数4のCarオブジェクトが作成されます。

●初期化の応用例

初期化はプログラムにおける重要なプロセスであり、特にSwiftのようなモダンなプログラム言語においては、様々な応用方法が存在します。

今回はSwiftの初期化の中でも、特に「応用例」として注目される部分を取り上げます。

具体的には、デザインパターンとしてよく知られる「Singletonパターン」と「Builderパターン」の初期化の方法を、サンプルコードを交えて詳しく解説していきます。

○サンプルコード10:Singletonパターンの初期化

Singletonパターンは、特定のクラスのインスタンスが一つしか生成されないことを保証するデザインパターンです。

ここでは、SwiftにおけるSingletonパターンの初期化のサンプルコードを紹介します。

class Singleton {
    static let shared = Singleton()
    private init() {}

    func someMethod() {
        // 何らかの処理
    }
}

このコードでは、sharedというstatic変数を通じて、Singletonクラスのインスタンスを取得します。

そして、初期化メソッドinitをprivateにすることで、このクラスの外部からのインスタンス生成を制限しています。

これにより、SingletonクラスのインスタンスはSingleton.sharedを通じてのみアクセス可能となります。

例として、次のように使用することができます。

let singletonInstance = Singleton.shared
singletonInstance.someMethod()

このように、Singletonパターンを利用することで、特定のクラスのインスタンスが一つしか生成されないことを保証することができます。

○サンプルコード11:Builderパターンの初期化

Builderパターンは、複雑なインスタンスの生成をサポートするデザインパターンです。

特に、複数のステップを経てインスタンスを生成する必要がある場合に有効です。

ここでは、SwiftにおけるBuilderパターンの初期化のサンプルコードを紹介します。

class Product {
    var name: String?
    var price: Double?

    class Builder {
        private var product = Product()

        func setName(_ name: String) -> Builder {
            product.name = name
            return self
        }

        func setPrice(_ price: Double) -> Builder {
            product.price = price
            return self
        }

        func build() -> Product {
            return product
        }
    }
}

このコードでは、Productクラス内にBuilderというネストされたクラスを持っています。

Builderクラスは、Productのインスタンスを段階的に生成するためのメソッドを提供しています。

例として、次のように使用することができます。

let product = Product.Builder().setName("Apple").setPrice(100.0).build()

このように、Builderパターンを利用することで、複雑なインスタンスの生成を簡潔かつ分かりやすく記述することができます。

●2段階の初期化

Swiftの2段階初期化は、特にクラスの初期化時に重要となるコンセプトです。

ここでは、2段階初期化の基本的な考え方とその応用について、具体的なサンプルコードを交えながら解説していきます。

○サンプルコード12:2段階初期化の基本

このコードでは、クラスの初期化を行う際の2段階初期化の基本的な流れを表しています。

この例では、スーパークラスとサブクラスの関係を持つクラスの初期化を行い、それぞれのクラスでの初期化の流れを確認しています。

class SuperClass {
    var value: Int
    init(value: Int) {
        self.value = value
        // 1段階目の初期化
    }
    func setup() {
        // 2段階目の初期化での設定
    }
}

class SubClass: SuperClass {
    var label: String
    init(value: Int, label: String) {
        self.label = label
        // 1段階目の初期化
        super.init(value: value)
        setup()
        // 2段階目の初期化
    }
}

この例のように、2段階初期化ではまずサブクラスのプロパティを初期化(1段階目)し、その後にスーパークラスの初期化を行った後で、追加の設定やメソッドの呼び出し(2段階目)を行います。

このコードを実行すると、まずSubClassのインスタンスが生成され、その際にvaluelabelという2つのプロパティが正しく初期化されることが確認できます。

○サンプルコード13:2段階初期化の応用

次に、2段階初期化の応用例を紹介します。

このコードでは、複数のサブクラスが存在する場面での2段階初期化の実装方法を表しています。

この例では、異なるサブクラスがそれぞれの初期化処理を持つシナリオを想定しています。

class SuperClass {
    var value: Int
    init(value: Int) {
        self.value = value
        // 1段階目の初期化
    }
    func setup() {
        // 2段階目の初期化での設定
    }
}

class SubClassA: SuperClass {
    var labelA: String
    init(value: Int, labelA: String) {
        self.labelA = labelA
        // 1段階目の初期化
        super.init(value: value)
        setup()
        // 2段階目の初期化
    }
}

class SubClassB: SuperClass {
    var labelB: String
    init(value: Int, labelB: String) {
        self.labelB = labelB
        // 1段階目の初期化
        super.init(value: value)
        setup()
        // 2段階目の初期化
    }
}

このコードを実行すると、SubClassASubClassBという2つのサブクラスのインスタンスがそれぞれ生成され、各サブクラスに応じたプロパティが正しく初期化されることが確認できます。

それぞれのサブクラスでは、独自のプロパティの初期化と、スーパークラスの初期化処理を呼び出すことで、2段階初期化の流れが実現されています。

●初期化の注意点と対処法

Swiftでのクラスや構造体の初期化は、そのオブジェクトのインスタンスが安全に使われるようにするための重要なプロセスです。

初期化の間に、プロパティが適切な初期値を持つように設定することで、オブジェクトは正しく機能します。

しかし、初期化のプロセスは複雑になることがあり、さまざまな注意点や問題が生じることがあります。

ここでは、初期化の際に考慮すべき注意点と、それに対する対処法を解説します。

○サンプルコード14:初期化失敗の対処

Swiftでは、初期化が失敗する可能性がある場合、オプションのイニシャライザを提供しています。

これにより、初期化が失敗した場合にnilを返すことができます。

このコードでは、整数の値を受け取り、それが0より大きい場合のみオブジェクトを初期化するクラスを表しています。

この例では、valueが0以下の場合、初期化は失敗してnilを返します。

class PositiveNumber {
    var value: Int

    init?(value: Int) {
        if value <= 0 {
            return nil
        }
        self.value = value
    }
}

let num1 = PositiveNumber(value: 5) // インスタンスが作成される
let num2 = PositiveNumber(value: -3) // nilを返す

この例のコードを実行すると、num1PositiveNumberのインスタンスを持ちますが、num2はnilになります。

○サンプルコード15:初期化時の無限ループの回避

初期化の際には、特にプロパティの相互参照や、自分自身を呼び出すような状況では無限ループに注意する必要があります。

このコードでは、ParentクラスとChildクラスを使って、相互参照による無限ループの問題を表しています。

この例では、ParentChildを持ち、ChildParentを参照しています。

initの中で、相互にインスタンスを生成しようとすると無限ループになる可能性があります。

class Parent {
    var child: Child?

    init() {
        self.child = Child(parent: self)
    }
}

class Child {
    weak var parent: Parent?

    init(parent: Parent) {
        self.parent = parent
    }
}

let parentInstance = Parent()

この例では、Childparentプロパティをweakとして定義することで、無限ループを回避しています。

weakは弱参照を意味し、循環参照を避けるための方法の一つです。

このコードを実行すると、parentInstanceは正常にParentのインスタンスを持ち、childプロパティも正常にChildのインスタンスを持つことができます。

●初期化のカスタマイズ方法

Swiftの初期化処理は柔軟性が高く、プロジェクトのニーズに合わせてカスタマイズが可能です。

ここでは、Swiftの初期化のカスタマイズ方法に関するサンプルコードを通して詳しく解説します。

○サンプルコード16:カスタム初期化デリゲートの導入

Swiftでは、初期化時に特定の処理を外部のデリゲートに委譲することができます。

これにより、初期化のプロセスを分離・独立させることが可能となります。

// 初期化デリゲートプロトコルの定義
protocol InitializationDelegate {
    func initializeData() -> String
}

// デリゲートを持つクラスの定義
class CustomInitClass {
    var data: String
    var delegate: InitializationDelegate?

    init(delegate: InitializationDelegate) {
        self.delegate = delegate
        self.data = delegate.initializeData()
    }
}

// デリゲートの具体的な実装例
class MyDelegate: InitializationDelegate {
    func initializeData() -> String {
        return "Initialized by MyDelegate"
    }
}

// クラスの初期化と実行
let myDelegateInstance = MyDelegate()
let myClassInstance = CustomInitClass(delegate: myDelegateInstance)
print(myClassInstance.data)  // "Initialized by MyDelegate" と表示されます。

このコードではInitializationDelegateプロトコルを使って、初期化の詳細な処理を外部のデリゲートに委譲しています。

この例ではMyDelegateというクラスがデリゲートとして実装され、CustomInitClassの初期化時にデリゲートを介してデータを初期化しています。

○サンプルコード17:初期化とFactoryメソッドの組み合わせ

Factoryメソッドは、オブジェクトの生成を隠蔽し、特定の条件やロジックに基づいてインスタンスを返すメソッドです。

Swiftでは、初期化とFactoryメソッドを組み合わせることで、柔軟なオブジェクト生成が可能となります。

// Factoryメソッドを持つクラスの定義
class Animal {
    var species: String

    private init(species: String) {
        self.species = species
    }

    static func createAnimal(type: String) -> Animal? {
        switch type {
        case "Dog":
            return Animal(species: "Dog")
        case "Cat":
            return Animal(species: "Cat")
        default:
            return nil
        }
    }
}

// Factoryメソッドの使用例
if let dog = Animal.createAnimal(type: "Dog") {
    print(dog.species)  // "Dog" と表示されます。
}

if let cat = Animal.createAnimal(type: "Cat") {
    print(cat.species)  // "Cat" と表示されます。
}

このコードではAnimalクラスにFactoryメソッドcreateAnimalを定義しています。

このメソッドを使用することで、"Dog""Cat"などの文字列を元にAnimalのインスタンスを生成しています。

Factoryメソッドを使用することで、クライアント側は実際の初期化の詳細を知らなくても、簡単にオブジェクトを生成することができます。

まとめ

Swiftの初期化処理は非常に柔軟であり、多様なプロジェクトや要件に合わせて適切にカスタマイズすることが可能です。

この記事では、Swiftの初期化の基本から、初期化時のプロパティ設定、継承との関連、そして応用的な初期化方法まで、17のサンプルコードを用いて具体的に解説しました。

特に、初期化のカスタマイズ方法において、デリゲートを使用した初期化やFactoryメソッドの組み合わせによる初期化方法は、大規模なアプリケーションや複雑なビジネスロジックを持つプロジェクトにおいて、柔軟かつ効率的な初期化を実現するための有効な手段となります。

Swiftでのアプリケーション開発を行う際には、適切な初期化方法を選択し、コードの可読性や保守性を高めることで、長期にわたるプロジェクトの成功に寄与することが期待されます。

今回学んだ知識を基に、より質の高いSwiftコードの実装を目指してみましょう。