11 Methodsメソッド

8654 ワード

メソッドは、特定のタイプに関連付けられた関数です.クラス、構造、および列挙は、特定のタイプのインスタンスを処理する特定のタスクおよび機能をカプセル化したインスタンスメソッドを定義できます.クラス、構造、および列挙は、タイプ自体に関連付けられたタイプメソッドを定義することもできます.タイプメソッドはObjective-Cのクラスメソッドに似ています.
構造と列挙は、CとObjective-Cとの主な違いであるSwiftでメソッドを定義することができる.Objective-Cでは、クラスはメソッドを定義できる唯一のタイプです.Swiftでは、クラスを定義するか、構造を定義するか、列挙するかを選択できますが、作成したタイプのメソッドを柔軟に定義できます.
Instance Methodsインスタンスメソッド
インスタンスメソッドは、特定のクラス、構造、または列挙されたインスタンスに属する関数です.これらのインスタンスの機能をサポートしたり、インスタンスのプロパティにアクセスしたり変更したり、インスタンスの用途に関連する機能を提供したりします.インスタンスメソッドは、関数で説明したように、関数と全く同じ構文を有します.
インスタンスメソッドは、その属するタイプのカッコとカッコで記述されます.インスタンス・メソッドは、このタイプの他のすべてのインスタンス・メソッドとプロパティに暗黙的にアクセスできます.インスタンス・メソッドは、そのタイプの特定のインスタンスでのみ呼び出されます.既存のインスタンスがない場合は、単独で呼び出すことはできません.
次の例では、アクションの発生回数を計算するために使用できる簡単な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
    

    関数パラメータには、名前(関数ボディに使用)とパラメータラベル(関数呼び出し時に使用)を同時に持つことができます.メソッドはTypeに関連付けられた関数にすぎないからです.
    The self Property self属性
    タイプの各インスタンスにはselfという暗黙的なプロパティがあり、インスタンス自体と完全に等価です.selfプロパティを使用して、独自のインスタンスメソッドで現在のインスタンスを参照できます.
    上記の例のincrement()メソッドは、次のように書くことができます.
    func increment() {
        self.count += 1
    }
    

    実際には、selfをコードに頻繁に記述する必要はありません.selfを明示的に記述していない場合、Swiftは、メソッドで既知のプロパティまたはメソッド名を使用するときに現在のインスタンスのプロパティまたはメソッドを参照していると仮定します.この仮定は,self.contではなくcountをCounterの3つのインスタンスメソッドで用いることによって実証した.
    このルールの主な例外は、インスタンスメソッドのパラメータ名がインスタンスのプロパティと同じ名前である場合に発生します.この場合、パラメータ名が優先され、属性をより限定的に参照する必要があります.selfプロパティを使用して、パラメータ名とプロパティ名を区別できます.
    ここでselfは、メソッドパラメータxとインスタンス属性xとの曖昧さを解消する.
    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")
    }
    

    self接頭辞がない場合、Swiftはxの両方の使用法がxというメソッドパラメータを参照していると仮定します.コンパイラがエラーを報告します.
    Modifying Value Type from Within Instance Methodsインスタンスメソッドから値タイプを変更
    構造と列挙は値タイプです.デフォルトでは、値タイプのプロパティはインスタンスメソッドから変更できません.
    ただし、特定のメソッドで値タイプ属性(構造体および列挙)を変更する必要がある場合は、そのメソッドの動作を変更することを選択できます.その後、メソッドはメソッドの内部から属性を変更(すなわち変更)することができ、メソッドが終了すると、その変更は元の構造に戻されます.このメソッドは、メソッドの終了時に既存のインスタンスを置き換える新しいインスタンスを暗黙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構造は、1つのPointインスタンスを一定数移動する可変moveBy(x:y:)メソッドを定義しています.この方法は、新しいポイントを返すのではなく、呼び出されたポイントを実際に変更します.mutatingキーワードは、属性を変更できるように定義に追加されます.
    構造タイプの定数に対してmutatingメソッドを呼び出すことはできません.これは、定数構造インスタンスのストレージ属性で説明されているように、変数属性であっても変更できないためです.
    let fixedPoint = Point(x: 3.0, y: 3.0)
    fixedPoint.moveBy(x: 2.0, y: 3.0)
    // this will report an error
    

    Assigning to self Within a Mutating Method可変メソッドでselfに値を割り当てる
    変更方法は、暗黙selfプロパティに新しいインスタンスを割り当てることができます.上の例は次のように書くことができます.
    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の値がターゲット位置に設定された新しい構造を作成します.このメソッドの代替バージョンを呼び出す最終結果は、以前のバージョンを呼び出す結果と全く同じになります.
    列挙の変更方法は、暗黙的な自己パラメータを同じ列挙とは異なる場合に設定できます.
    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
    

    この例は三状態スイッチの列挙を定義する.next()メソッドが呼び出されるたびに、スイッチは3つの異なる電源状態(off、low、high)の間で循環する.
    Type Methods
    前述したように、インスタンスメソッドは、特定のタイプのインスタンスに対して呼び出されるメソッドである.また、タイプ自体で呼び出す方法を定義することもできます.これらのタイプのメソッドをタイプメソッドと呼びます.メソッドのfuncキーワードの前にstaticキーワードを記述することによってタイプメソッドを示す.クラスはまたclassキーワードを使用して、サブクラスがスーパークラスを上書きする方法を実装することもできます.
    Objective-Cでは、Objective-Cクラスのタイプレベルを定義する方法しかありません.Swiftでは、すべてのクラス、構造、列挙のタイプレベルメソッドを定義できます.各タイプのメソッドは、サポートされるタイプの役割ドメインを明示的に定義します.
    タイプメソッドは、インスタンスメソッドなどのポイント構文呼び出しを使用します.ただし、タイプのインスタンスに対してタイプメソッドを呼び出すのではなく、タイプに対してタイプメソッドを呼び出す必要があります.次に、SomeClassというクラスでタイプメソッドを呼び出す方法を示します.
    class SomeClass {
        class func someTypeMethod() {
            // type method implementation goes here
        }
    }
    SomeClass.someTypeMethod()
    

    タイプメソッドのボディでは、暗黙selfプロパティはタイプ自体を参照し、そのタイプのインスタンスではありません.これは、selfを使用して、インスタンスプロパティとインスタンスメソッドパラメータのように、タイププロパティとタイプメソッドパラメータの曖昧さを解消できることを意味します.
    より一般的には、タイプメソッド本体で使用される任意の非限定メソッドおよび属性名は、他のタイプレベルのメソッドおよび属性を参照する.タイプメソッドは、タイプ名を接頭辞として使用する必要がなく、別のメソッドの名前で別のタイプメソッドを呼び出すことができます.同様に、構造および列挙上のタイプメソッドは、タイプ名接頭辞を使用せずにタイプ属性の名前を使用することによってタイプ属性にアクセスすることができる.
    次の例では、ゲームの異なるレベルまたはフェーズのプロセスを追跡できるLevelTrackerという構造を定義します.これはシングルゲームですが、1つのデバイスに複数のプレイヤーに情報を格納することができます.
    ゲームのすべてのレベル(第1レベルを除く)は、最初のゲームでロックされます.プレイヤーが1つのレベルを完了するたびに、デバイス上のすべてのプレイヤーはそのレベルをロック解除することができます.LevelTracker構造は、タイププロパティとメソッドを使用して、どのレベルのゲームがロック解除されたかを追跡します.また、1人のプレイヤーの現在のレベルも追跡します.
    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
            }
        }
    }
    

    レベルトラッカー構造は、任意のプレイヤーがロックを解除した最高レベルを追跡します.この値はhighestUnlockedLevelというタイプのプロパティに格納されます.
    LevelTrackerでは、highestUnlockedLevelプロパティを使用する2つのタイプの関数も定義されています.最初のタイプの関数はunlock(:)と呼ばれ、新しいレベルをロック解除するたびにhighestUnlockedLevelの値が更新されます.2つ目はisunlock(:)という便利なタイプの関数で、特定のレベル番号がロック解除された場合、trueが返されます.(これらのタイプのメソッドは、leveltrack.highestUnlockedLevelとして記述する必要がなく、highestUnlockedLevelタイプのプロパティにアクセスできます.)
    タイプ属性とタイプメソッドに加えて、LevelTrackerはプレイヤーのゲーム中のプロセスを追跡します.currentLevelというインスタンス属性を使用して、プレイヤーが現在遊んでいるレベルを追跡します.
    currentLevelプロパティの管理を支援するために、LevelTrackerはadvance(To:)というインスタンスメソッドを定義します.このメソッドは、currentLevelを更新する前に、リクエストの新しいレベルがロック解除されているかどうかを確認します.advance(to:)メソッドは、currentLevelを実際に設定できるかどうかを示すブール値を返します.戻り値を無視するためにadvance(to:)メソッドを呼び出すコードは必ずしもエラーではないため、この関数は@discardableResultプロパティとしてマークされます.このアトリビュートの詳細については、キーワードアトリビュートAttributesを参照してください.
    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:)という方法も提供されています.この方法は、すべてのプレイヤーの次の関門をロック解除し、プレイヤーのプロセスを更新して次の関門に移動します.(advance(to:)のブール戻り値は無視されます.leveltrackが呼び出されたことが知られているためです.unlock(:)前の行でロックを解除します.
    新しいプレイヤーの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"
    

    <