メソッドは、特定の型に結び付けられた関数です。クラス・構造体・列挙型は全てインスタンスメソッドを定義できます。インスタンスメソッドはその型のインスタンスから呼び出す特定の機能をカプセル化したものです。クラス・構造体・列挙型はタイプメソッドを定義することもできます。タイプメソッドは型そのものと結び付けられています。
インスタンスメソッド
インスタンスメソッドは特定のクラス・構造体・列挙型のインスタンスに属する関数です。インスタンスのプロパティにアクセスしたりインスタンスのプロパティを修正する手段を提供したり、インスタンスの目的に関係した機能を提供します。インスタンスメソッドの定義は関数の構文と同じです。
インスタンスメソッドの定義は型定義のブロック内に記述します。インスタンスメソッドは、その型の全てのインスタンスメソッドとプロパティに暗黙的にアクセスできます。インスタンスメソッドはその型のインスタンスからのみ呼び出すことができます。インスタンス無しで独立して呼び出すことはできません。
下のサンプルはシンプルなカウンターのクラス定義です。
sample1-1
class Counter { var count = 0 func increment() { count += 1 } func increment(by amount: Int) { count += amount } func reset() { count = 0 } } let counter = Counter() // the initial counter value is 0 counter.increment() print(counter.count) //1 counter.increment(by: 5) print(counter.count) //6 counter.reset() print(counter.count) //0
Counterクラスは三つのインスタンスメソッドを定義していまする
- increment() countプロパティを1加算する。
- increment(by:Int) 引数で指定された数countプロパティを加算する
- reset() countプロパティを0にリセットする
メソッドの引数は、関数と同様、パラメータネームとアーギュメントラベルの両方を使用できます。
selfプロパティ
全ての型のインスタンスは暗黙的なプロパティとしてselfを有しています。selfプロパティはそのインスタンス自身を指します。現在のインスタンスを参照するためにインスタンスメソッドの中でselfプロパティを使用することができます。
sample1-1のincrement()メソッドは下のように記述することができます。
func increment() { self.count += 1 }
実際には、コードを書くときにselfは頻繁に使う必要はありません。メソッドの中でselfを記述しない場合、Swiftは現在のインスタンスのプロパティ・メソッドを参照しているのだろうと推定します。ですのでselfを記述していないsample1-1の三つのインスタンスメソッドは問題なく機能します。
selfが必要なケースは、インスタンスメソッドのパラメータネームと、そのインスタンスのプロパティ名に同じ名前が存在する場合です。
sample1-2
struct Point { var x = 0.0, y = 0.0 func isToTheRightOf(x: Double) -> Bool { return self.x > x } } let somePoint = Point(x: 4.0, y: 5.0) if somePoint.isToTheRightOf(x: 1.0) { print("This point is to the right of the line where x == 1.0") } // Prints "This point is to the right of the line where x == 1.0"
sample1-2の4行目でselfを記述しないと、4行目のxは二つとも3行目のメソッド引数のxを参照しますので、意図した挙動とは違う挙動になってしまいます。
インスタンスメソッド内から値型を変更する
構造体と列挙型は値型です。デフォルトでは、値型のプロパティは、その値型のインスタンスメソッドから変更できません。
工事中🏗
タイプメソッド
インスタンスメソッドは、特定の型のインスタンスから呼び出すメソッドですが、(インスタンスからではなく)型そのものから呼び出すメソッドを定義することもできます。タイプメソッドであることを示すため、メソッド定義のfuncキーワードの前にstaticキーワードを記述します。クラスでは、staticキーワードの代わりにclassキーワードを使うこともできます。classキーワードを使うと、スーパークラスで定義したクラスメソッドをサブクラスがオーバーライドできるようになります。
NOTE
Swiftではタイプメソッドを全てのクラス・構造体・列挙型で定義できます。それぞれのタイプメソッドは定義された型でスコープされます。
タイプメソッドはインスタンスメソッドのようにドット( . )を使用して呼び出します。タイプメソッドは型から呼び出すもので、インスタンスから呼び出すことはできません。下のサンプルはsomeClassからタイプメソッドを呼び出す方法です。
class SomeClass { class func someTypeMethod() { // タイプメソッドの実装 } } SomeClass.someTypeMethod()
タイプメソッドのボディ内で、暗黙的なselfプロパティは(インスタンスではなく)型そのものを参照します。インスタンスメソッドのselfの部分で説明した方法と同じ方法で、「タイププロパティ」と「タイプメソッドの引数」が同じ名前の場合に識別するためにselfを使うことができます。
もっと一般的なケースで言えば、タイプメソッド内で、selfを付けていないメソッド・プロパティ名は、他のタイプメソッド・タイププロパティを参照します。タイプメソッドは他のタイプメソッドを、(型名を前置することなく)他のタイプメソッドの名前のみで呼び出せます。構造体や列挙型でも同様に、タイプメソッドは、型名無しでプロパティ名のみでタイププロパティにアクセスできます。
sample1-3ではLevelTrackerという名の構造体を定義しています。LevelTrackerはゲーム内のプレイヤーのレベルやステージの進捗を追跡します。一人用のゲームですが、一つのデバイスで複数プレイヤーの情報を格納できます。
初めてゲームがプレーされる時点で、全てのゲームのレベル(レベル1をのぞいて)はロックされています。プレーヤーがそのレベルを終了した時はいつでも、そのレベルはそのデバイスの全てのプレーヤーにとってアンロックとなります。
工事中🏗
LevelTracker構造体はいずれかのプレーヤーがアンロックした最高レベルを追跡し続けます。その値はhighestUnlockedLevelという名のタイププロパティに格納されます。
LevelTrackerはhighestUnlockedLevelプロパティを扱う二つのタイプメソッドを定義しています。一つ目はunlock(_:)という名のタイプメソッドで、新しいレベルがアンロックされた時にhighestUnlockedLevelの値を更新するメソッドです。二つ目のタイプメソッドはisUnlocked(_:)で、特定のレベル値がすでにアンロックされているかどうかを判定します。すでにアンロックされている場合trueを返します。(これらのタイプメソッドからhighestUnlockedLevelには、LevelTracker.highestUnlockedLevelと記述しなくてもアクセスできる点に注意してください。)
タイププロパティとタイプメソッドに加えて、LevelTrackerは個々のブレーヤーのゲームの進捗度を追跡します。currentLevelという名のインスタンスプロパティを使い、現在プレーしているプレーヤーのレベルを追跡します。
currentLevelプロパティの管理を助けるため、LevelTrackerはadvance(to:)という名のインスタンスメソッドを定義しています。currentLevelを更新する前に、このメソッドはリクエストされた新しいレベルがすでにアンロックされているかをチェックします。advance(to:)メソッドはcurrentLevelに値をセットできるかどうかを示すBool型の値を返します。
LevelTracker構造体はPlayerクラスで使用されます。以下のように個々のプレーヤーの進捗度の追跡と更新のために使用されます。
class Player { var tracker = LevelTracker() let playerName: String func complete(level: Int) { LevelTracker.unlock(level + 1) tracker.advance(to: level + 1) } init(name: String) { playerName = name } }
Playerクラスは自身の進捗度を追跡するための新しいLevelTrackerクラスのインスタンスを生成します。Playerクラスはcomplete(level:)メソッドも定義しています。このメソッドはプレーヤーがその時のレベルを完了した時に呼び出されます。このメソッドは、全てのプレーヤーに対して次のレベルをアンロック(開放)し、そのプレーヤーを次のレベルへレベルアップさせます。(このサンプルではadvanceメソッド実行の前の行でLevelTracker.unlock(_:)メソッドを呼び出しています。それによりadvance(to:)メソッドが常にtrueを返すのは自明ですので、advance(to:)メソッドの返り値のBool値は無視しています。)
新しいプレーヤーとして、Playerクラスのインスタンスを生成し、そのプレーヤーがレベル1を完了した時に何が起きるかを見てみましょう。
var player = Player(name: "Argyrios") player.complete(level: 1) print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)") // Prints "highest unlocked level is now 2"
もし二番目のプレーヤーBetoを作り、(complete(level:)メソッドを呼び出さずに)advance(to:)メソッド呼び出しでレベルを上げようとしても、BetoのcurrentLevelは更新されず、
“level 6 has not yet been unlocked”
のメッセージが表示されてレベルアップは失敗します。
レベルアップにはcomplete(level:)メソッドの呼び出しが必須、ということ。
参考
https://docs.swift.org/swift-book/LanguageGuide/Methods.html