Swift 5.8 日本語化計画 : Swift 5.8
メソッド
メソッド は、特定の型に関連する関数です。クラス、構造体、および列挙型は、与えられた型のインスタンスを操作するための特定のタスクや機能をカプセル化する、インスタンスメソッドを定義することができます。クラス、構造体、および列挙型もまた型自体に関連する型メソッドを定義できます。型メソッドは、Objective-C のクラスメソッドに似ています。
Swift では構造体や列挙型がメソッドを定義できるという事実は、C や Objective-C との大きな違いです。Objective-C では、クラスはメソッドを定義することができる唯一の型です。Swift では、クラス、構造体、または列挙型を定義するかどうかを選択し、作成する型でメソッドを定義する柔軟性もあります。
インスタンスメソッド
インスタンスメソッド は、特定のクラス、構造体、または列挙型のインスタンスに属している関数です。これらは、インスタンスの目的に関連する機能を提供することによって、またはインスタンス・プロパティにアクセスし、変更する方法を提供することによって、それらのインスタンスの機能をサポートします。関数 で説明したように、インスタンスメソッドは、関数とまったく同じ構文を持っています。
それが属する型の開閉括弧内にインスタンスメソッドを記述して下さい。インスタンスメソッドは、その型の他のすべてのインスタンスメソッドとプロパティへの暗黙のアクセス権を持っています。インスタンスメソッドは、それが属する型の特定のインスタンスでのみ呼び出すことができます。これは、既存のインスタンスなしに孤立して呼び出すことはできません。
ここでは、アクションが発生した回数をカウントするために使用できる、簡単な Counter クラスを定義する例を示します。
- class Counter {
- var count = 0
- func increment() {
- count += 1
- }
- func increment(by amount: Int) {
- count += amount
- }
- func reset() {
- count = 0
- }
- }
Counter クラスは3つのインスタンスメソッドを定義しています。
- increment() はカウンタを 1 増分します。
- increment(by: Int) は指定した整数量だけカウンタを増分します。
- reset() はカウンタをゼロにリセットします。
Counter クラスはまた、現在のカウンタ値を追跡するために、変数プロパティである count も宣言しています。
プロパティと同じドット構文でインスタンスメソッドを呼び出して下さい。
- let counter = Counter()
- // the initial counter value is 0
- counter.increment()
- // the counter's value is now 1
- counter.increment(by : 5)
- // the counter's value is now 6
- counter.reset()
- // the counter's value is now 0
関数のパラメータには、(関数の本体内で使用するための) 名前と(関数を呼び出すときに使用するために) 引数のラベルの両方があり、それは 関数引数のラベルとパラメータ名 で説明しました。同じことは、メソッドのパラメータにも当てはまり、というのもメソッドは型に関連する関数に過ぎないからです。
self プロパティ
型のすべてのインスタンスには、インスタンス自体とまったく同じ self と言う暗黙のプロパティがあります。独自のインスタンスメソッド内の現在のインスタンスを参照するために、self プロパティを使用して下さい。
上記の例の increment() メソッドは、以下のように書くことができます:
- func increment() {
- self.count += 1
- }
実際には、頻繁にコードの中で self と書く必要はありません。明示的に self を記述しなくても、Swift は、メソッド内で周知のプロパティやメソッド名を使用するたびに、現在のインスタンスのプロパティまたはメソッドを参照していることを前提とします。この前提は、Counter の3つのインスタンスメソッド内の (self.count ではなく) count を使用することで例示されています。
インスタンスメソッドのパラメータ名が、そのインスタンスのプロパティと同じ名前を持つ場合は、この規則の主な例外です。この状況では、パラメータ名が優先され、より適格な方法でプロパティを参照することが必要となります。パラメータ名とプロパティ名を区別するために、self プロパティを使用して下さい。
ここでは、x というメソッドのパラメータと、それも x という名前のインスタンスプロパティを区別するのに self を使用しています。
- 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"
self の接頭辞がないと、Swift は、x の両方の使用とも、x と言うメソッドパラメータを参照していることを前提とします。
インスタンスメソッド内から値型を変更
構造体と列挙型は 値型 です。デフォルトでは、値型のプロパティは、そのインスタンスメソッド内から変更することはできません。
ただし、特定のメソッド内の構造体または列挙型のプロパティを変更する必要がある場合は、そのメソッドの動作を mutating (変異) する事を選択することができます。メソッドはその後そのプロパティをメソッド内から変異 (つまり、変更) することができ、メソッドが終了したときにそれが行なった変更を全て元の構造体に書き戻します。メソッドはまた、その暗黙の self プロパティに、完全に新しいインスタンスを代入することもでき、そのメソッドが終了したときに、この新しいインスタンスは既存のものを置き換えます。
そのメソッドの func キーワードの前に mutating キーワードを配置することによって、この動作を選択できます。
- struct Point {
- var x = 0.0, y = 0.0
- mutating func moveBy(x deltaX: Double, y deltaY: Double) {
- x += deltaX
- y += deltaY
- }
- }
- var somePoint = Point(x: 1.0, y: 1.0)
- somePoint.moveBy(x: 2.0, y: 3.0)
- print("The point is now at (\(somePoint.x), \(somePoint.y))")
- // prints "The point is now at (3.0, 4.0)"
上記の Point 構造体は、一定量 Point インスタンスを移動する、mutating moveBy(x:y:) メソッドを定義しています。新しいポイントを返す代わりに、このメソッドは実際にそれが呼び出されたポイントを変更します。mutating キーワードが、そのプロパティを変更できるように、その定義に追加されています。
そのプロパティが変更できないので、定数構造体インスタンスの格納されたプロパティ で説明したように、それらが変数プロパティであっても、構造体型の定数では変異メソッド (mutating method) を呼び出せないので注意してください。
- let fixedPoint = Point(x: 3.0, y: 3.0)
- fixedPoint.moveByX(2.0, y: 3.0)
- // this will report an error
変異メソッド内で self に代入
変異メソッドは暗黙の self プロパティに、完全に新しいインスタンスを代入することができます。上に示した Point の例は、代わりに以下のように書けます:
- struct Point {
- var x = 0.0, y = 0.0
- mutating func moveBy(x deltaX: Double, y deltaY: Double) {
- self = Point(x: x + deltaX, y: y + deltaY)
- }
- }
変異する moveBy(x:y:) メソッドのこのバージョンは、その x と y の値が目的の場所に設定されている、新しい構造体を作成します。このメソッドの代替バージョンを呼び出した最終結果は、以前のバージョンを呼び出すのとまったく同じです。
列挙型の変異メソッドは、同じ列挙型とは別のケースであるように暗黙の self パラメータを設定できます。
- enum TriStateSwitch {
- case off, low, high
- mutating func next() {
- switch self {
- case .off:
- self = .low
- case .low:
- self = .high
- case .high:
- self = .off
- }
- }
- }
- var ovenLight = TriStateSwitch.low
- ovenLight.next()
- // ovenLight is now equal to .high
- ovenLight.next()
- // ovenLight is now equal to .off
この例では、3つの状態の、あるスイッチの列挙型を定義しています。3つの異なる電気の状態 (off、low と high) の間のスイッチのサイクルはその next() メソッドが呼び出されるたびに切り替わります。
型メソッド
上記のように、インスタンスメソッドは、特定の型のインスタンスで呼び出されるメソッドです。また、型自体で呼び出されるメソッドを定義することもできます。この種のメソッドは 型メソッド と呼ばれます。メソッドの func キーワードの前に static キーワードを書くことによって、型メソッドである事を示せます。クラスは、サブクラスがそのメソッドのスーパークラスの実装をオーバーライドできるようにするため、class キーワードを代わりに使用する事ができます。
型メソッドは、インスタンスメソッドのように、ドット構文で呼び出されます。しかし、その型のインスタンス上ではなく、型の上で型メソッドを呼び出して下さい。ここで、SomeClass というクラス上の型メソッドを呼び出す方法を示します。
- class SomeClass {
- class func someTypeMethod() {
- // type method implementation goes here
- }
- }
- SomeClass.someTypeMethod()
型メソッドの本体内では、暗黙の self プロパティはその型のインスタンスよりむしろ、その型自体を参照します。これは、インスタンス・プロパティとインスタンスメソッドのパラメータの場合と同様に、型プロパティと型メソッドのパラメータとの間で区別するために self を使用できることを意味します。
より一般的には、型メソッドの本体内で使用するすべての資格のないメソッドとプロパティ名は、他の型レベルのメソッドとプロパティを参照します。型メソッドは、型の名前に接頭辞をつける必要なしに、他のメソッドの名前を持つ別の型メソッドを呼び出すことができます。同様に、構造体と列挙型上の型メソッドは、型名の接頭辞なしで型プロパティの名前を使用して、型プロパティにアクセスできます。
以下の例は、ゲームの異なるレベルまたはステージを通して、プレイヤーの進行状況を追跡する LevelTracker という構造体を定義しています。それは、一人プレイヤー用のゲームですが、1つのデバイスで複数のプレーヤーの情報を格納することができます。
ゲームを最初に遊んだとき (レベル1から離れた) ゲームのレベルはすべて、ロックされています。プレイヤーが、レベルを終了するたびに、そのレベルは、デバイス上のすべてのプレーヤー用にロック解除されます。LevelTracker 構造体は、ゲームのどのレベルがロック解除されたかを追跡するために、型プロパティとメソッドを使用します。また、個々のプレーヤーの現在のレベルも追跡します。
- struct LevelTracker {
- static var highestUnlockedLevel = 1
- var currentLevel = 1
- static func unlock(_ level: Int) {
- if level > highestUnlockedLevel { highestUnlockedLevel = level }
- }
- static func isUnlocked(_ level: Int) -> Bool {
- return level <= highestUnlockedLevel
- }
- @discardableResult
- mutating func advance(to level: Int) -> Bool {
- if LevelTracker.isUnlocked(level) {
- currentLevel = level
- return true
- } else {
- return false
- }
-     }
- }
LevelTracker 構造体は、任意のプレイヤーがロック解除した最高レベルを追跡します。この値は、 highestUnlockedLevel という型プロパティに格納されます。
LevelTracker も highestUnlockedLevel プロパティで動作する2つの型関数を定義しています。最初のものは、新しいレベルがロック解除されたときいつでも highestUnlockedLevel の値を更新する unlock(_:) と言う型関数です。二つ目は、特定のレベル数がすでにロック解除されている場合 true を返す isUnlocked(_:) と言うコンビニエンス型関数です。(これら型メソッドは、LevelTracker.highestUnlockedLevel と書く必要なしに highestUnlockedLevel の型プロパティにアクセスできることに注意してください。)
その型プロパティと型メソッドに加え、LevelTracker はゲームを通じて、個々のプレイヤーの進行状況を追跡します。これは、プレイヤーが現在遊んでいるレベルを追跡するために currentLevel というインスタンスプロパティを使用します。
currentLevel プロパティの管理を支援するために、LevelTracker は advance(to:) というインスタンスメソッドを定義しています。currentLevel を更新する前に、このメソッドは要求された新しいレベルがすでにロック解除されているかどうかチェックします。advance(to:) メソッドは、それが実際に currentLevel を設定することができたかどうかを示すブール値を返します。戻り値を無視するために adbance(to:) メソッドを呼び出すのはコードの間違いでは必ずしもないので、この関数は @discardableResult 属性でマークされています。この属性の詳細については、属性 を参照してください。
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 の新しいインスタンスを作成します。また、プレイヤーが特定のレベルを完了するたびに呼び出される complete(level:) と言うメソッドも提供します。このメソッドは、すべてのプレーヤーの次のレベルのロックを解除し、プレイヤーの進行状況を更新し、次のレベルに彼らを移動します。(前の行の LevelTracker.unlock(_:) への呼び出しによってそのレベルのロックが解除されたことがわかるので、advance(to:) のブールの戻り値は、無視されます。)
新しいプレイヤーの 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"
まだゲーム内のどのプレイヤーによってもロック解除されていないレベルに移動しようとする、第2のプレイヤーを作成した場合、プレイヤーの現在のレベルを設定する試みは失敗します。
- player = Player(name: "Beto")
- if player.tracker.advance(to: 6) {
- print("player is now on level 6")
- } else {
- print("level 6 has not yet been unlocked")
- }
- // prints "level 6 has not yet been unlocked"
前:プロパティ 次:サブスクリプト
トップへ
トップへ
トップへ
トップへ