Codable Tech Blog

iPhoneアプリケーション開発と AWS(Amazon Web Service)活用に関する記事を配信

クラス

Swiftにおいて、構造体とクラスはプロパティとメソッドをもつオブジェクトです。いくつかの違いがありますが、まず、知っておいてほしいのは構造体は値型、クラスは参照型であるという点です。値型と参照型の違いは変数や定数への値の格納方法です。値型の場合、変数や定数に値がそのまま代入されます。一方、クラスの場合、値そのものではなく、値が格納されている場所(アドレス)の値が格納されている点です。
この違いはプログラム上、どのような違いをもたらすかはクラスの解説の中で説明していきます。

クラス定義

クラスは次のように定数・変数と関数をグループ化した形で定義されます。クラスで定義する定数・変数はプロパティ、関数はメソッドと呼ばれます。クラスは次のような構文で定義できます。

class クラス名 {
    //プロパティ
    //メソッド
}

次の例はString型のnameプロパティとInt型のageプロパティをもつPersonクラスの例です。

クラスのインスタンス化

クラスインスタンス作成方法としてもっとも簡単なのは空の括弧をつけてインスタンスを作成する方法です。

クラス名()

次の例はPersonクラスをインスタンス化した例です。

この例では、Personクラスのインスタンスを生成してperson変数に代入しています。

プロパティへのアクセス

プロパティにはドット構文を利用してアクセスします。

インスタンス名.プロパティ名

次の例は、ドット構文を使用してPersonクラスのインスタンスであるpersonからプロパティnameとageにアクセスしています。

値の代入

値の代入もドット構文をつかっておこなうことができます。

この例ではnameに"Yamada Taro"、ageに18を代入して、その結果を出力しています。なお、クラスの場合、定数としてインスタンスを宣言しても値を変更することが可能です。クラスのインスタンスは参照型であることを思い出してください。参照型はインスタンスの参照先を格納する型でした。つまり、定数となるのはインスタンスの参照先です。ですので、定数として宣言した場合、別のインスタンスを参照することはできませんが、参照先であるインスタンス自体は定数ではないため、値の変更ができるのです。

各プロパティの前にselfキーワードがついています。これはインスタンス自身を表します。つまり、self.nameと記述した場合、自身が保持するnameプロパティを示すことになります。selfプロパティの詳細はselfプロパティを参照してください。

初期化処理(イニシャライザ)

初期化処理(イニシャライザ)の実装

初期化はクラスを利用するための準備処理です。初期化処理はイニシャライザで行います。イニシャライザはインスタンス生成時に必ず呼ばれるメソッドで、次のようにinitというメソッド名で実装します。

init(パラメータ,...)

initメソッドの中でプロパティを適切な値に初期化します。次の例ではPersonクラスのnameとageをそれぞれ初期化しています。

初期化処理のカスタマイズ

イニシャライザはパラメータを受け取る形で実装することもできます。パラメータ付きのイニシャライザであれば、呼び出し元がインスタンスを生成する時、値を受け取ることができます。なお、イニシャライザはインスタンスメソッドのように一意な名前をもつことができません。このため、初期化メソッドを区別するためにパラメータ名とパラメータの型が重要になってきます。パラメータを持つinitメソッドは次のように定義できます。

この例ではnameパラメータの値をnameプロパティ、ageパラメータの値をageプロパティに代入しています。initメソッドが2つありますが、受け取るパラメータがそれぞれ異なるため問題ありません。このように、パラメータが異なる同じメソッド名のメソッドを複数個、定義できる機能を「オーバーロード」と呼びます。
イニシャライザでは、外部パラメータがない場合、自動でローカルパラメータと同じ名前の外部パラメータを自動でつけてくれます。このため、パラメータ付きのイニシャライザでインスタンスの初期化処理を行う場合、次のように呼び出します。

デフォルトイニシャライザ

Swiftではクラスにひとつもイニシャライザが提供されていない場合、デフォルトイニシャライザが提供されます。デフォルトイニシャライザはデフォルト値にしたがって、新しいインスタンスを作成します。

デフォルトイニシャライザ利用するにもかかわらず、プロパティにデフォルト値を与えていない場合、コンパイルエラーになるので注意してください。

上記の例ではnameプロパティにデフォルト値を与えていないため、コンパイルエラーが発生しています。デフォルトイニシャライザを利用する場合はプロパティにデフォルト値を与えることを忘れないようにしましょう。

インスタンスメソッド

インスタンスメソッドの定義

インスタンスメソッドは特定のインスタンスに属する関数です。インスタンスメソッドの文法は関数と同じで、funcキーワードを利用して定義します。クラスにインスタンスメソッドを追加する構文は次のようになります。

class 構造体名{
    func メソッド名(パラメータ名:パラメータの型,...){
        ...
    }
}

インスタンスメソッドはクラスブロック{ }の中に記述します。インスタンスメソッドはインスタンスなしにはコールできません。また、クラスのインスタンスメソッドはクラスから生成されたインスタンスだけがコールすることができます。次の例ではクラスPersonにprintSelfIntroductionメソッドを追加しています。

printSelfIntroductionメソッドはnameプロパティとageプロパティを利用して自己紹介文を出力するメソッドです。このprintSelfIntroductionメソッドはクラスPersonから生成されたインスタンスのみ利用できるメソッドになります。

インスタンスメソッドの呼び出し

インスタンスメソッドはプロパティ同様、ドット構文を利用することで呼び出し可能です。

インスタンス名.メソッド名(パラメータ,...)

次の例ではPersonクラスのインスタンスを生成して、printSelfIntroductionメソッドを呼び出しています。

printSelfIntroductionメソッドはnameとageの値を利用した自己紹介文を出力します。nameには「Hanako Sakura」、ageには20が設定されていますのでコンソール上に「私の名前はHanako Sakuraです。年齢は20歳です。」と出力されます。

メソッドのローカルパラメータと外部パラメータ

ローカルパラメータと外部パラメータの関係は構造体で解説しているメソッドのローカルパラメータと外部パラメータを参照してください。構造体、クラスで共通のルールが採用されています。

selfプロパティ

selfプロパティは構造体で解説しているselfプロパティを参照してください。構造体、クラスで共通のルールが採用されています。

インスタンス破棄時の処理(デイニシャライザ)

通常、インスタンスを破棄する処理は自動で行ってくれますのでなにも書く必要がありません。ただ、まれにインスタンス消滅時に処理を行いたい時があります。例えば、インスタンスが消滅する時にファイルにデータを保存したい場合などです。こういった場合に役立つのがデイニシャライザです。

デイニシャライザの実装

インスタンスが破棄されるタイミングで呼ばれるメソッドのことをデイニシャライザと呼びます。デイニシャライザはインスタンスが消滅される前に呼び出されます。デイニシャライザは次のようにdeinitメソッドを実装します。

deinit{ ... }

deinitメソッドはインスタンスが破棄されるタイミングで自動で呼ばれるメソッドです。このため、自分でdeinitメソッドを呼ばないようにしてください。
次の例はPersonクラスにdeinitメソッドを追加した例です。

デイニシャライザの動作確認

インスタンスがどこからも参照されなくなった時、解放されます。では、参照が切るにはどうしたらよいのでしょうか。Swift参照が切れるタイミングは2つあります。1つは宣言した変数・定数がスコープ(変数の有効範囲)から外れた時です2つ目はインスタンスが代入されている変数にnilが代入された場合です。この2つのタイミングでインスタンスへの参照が切れ、だれからも参照されなくなったインスタンスは解放されることになります。次の例ではtest関数を作成し、その中でPersonクラスのインスタンスを生成しています。

nilを代入した結果、Personクラスのインスタンスはだれからも参照されなくなったので、インスタンスが破棄されます。ただ、PlayGrounds環境下ではdeinitは動作しません。動作を確認したい場合はコンソールアプリケーションプロジェクトかiOSアプリケーションプロジェクトを作成して、上記のコードを実装、実行してみてください。

タイプメソッド

クラスのインスタンスとはクラスから生成されるオブジェクトのことでした。Swiftではクラス自身もオブジェクトとして認識されています。両者の違いはクラス自身は1つしか存在しませんが、クラスから生成されるインスタンスは複数個存在する点です。クラスもオブジェクトとして認識されていますので、クラスもプロパティとメソッドを持つことができます。このクラスがもつプロパティはタイププロパティと呼ばれ、メソッドはタイプメソッドと呼ばれます。しかし、クラスの場合、タイププロパティをもつことはできません。(厳密にいえば、コンピューティッドプロパティのタイププロパティは持つことが出来ますが、ここでは割愛します)
タイプメソッドはfuncキーワードの前にclassキーワードをつけることで定義することができます。

class クラス名 {
    class func メソッド名(パラメータ名,...)
}

タイププロパティやタイプメソッドにアクセスする時は、次のように、ドット構文を利用してアクセスします。

クラス名.メソッド名(パラメータ,...)

ドット記法で呼び出す時、インスタンス名ではなく、クラス名であることに注意してください。
次の例ではクラスPersonにタイプメソッドprintPersonInfoを定義しています。このメソッドは単純に呼び出された時に「Personクラスは人の情報を定義したクラスです」というメッセージを表示するメソッドです。

printPersonInfoはタイプメソッドですので、インスタンスを生成せずに呼び出すことができます。下記ではPerson.printPersonInfo()のようにメソッドを呼び出しています。このようにタイプメソッドはクラスに所属するメソッドですので、インスタンスを作成することなく呼び出せます。

クラス型のインスタンス比較

アイデンティティ演算子

二つの変数もしくは定数が同じインスタンスを参照しているかどうかを確認するのにアイデンティティ演算子が役に立ちます。アイデンティティ演算子には「===」と「!==」の2つがあります。

===

===演算子は同じクラスのインスタンスを参照していればtrueを返却します。

!==

!==演算子は同じクラスのインスタンスを参照していなければtrueを返却します。
次の例ではPersonクラスのインスタンスを作成し、同一のインスタンスかどうかをアイデンティティ演算子を利用して確認しています。

補足)==と===の違い

アイデンティティ演算子===は同じインスタンスを参照しているかどうかだけをチェックしています。一方、比較演算子==はインスタンスが保持しているプロパティが同じかどうかをチェックしています。ただし、比較演算子==は、型によっては全てのプロパティの値が同じでなくても値が等しいと判定される場合があります。これは、型ごとにどの値が等しければtrueとするかを決めているためです。

クラスと構造体の選択

クラスと構造体は次の基準で使い分けると良いでしょう。迷ったらクラスを選択すればよいでしょう。
メソッドを持たず、関連するデータをグループ化したいだけならば、構造体を選択するとよいでしょう。
メソッドに値を受け渡す時、値がコピーされたほうが望ましければ構造体を選択しましょう。
構造体は継承することができません。継承を利用したい場合はクラスを選択するとよいでしょう。