メソッド

Swiftでは、クラスだけでなく、構造体や列挙型にもメソッド(関数)を持たせることができます。また、Objective-Cのクラスメソッドにあたる型メソッドも、クラス、構造体、列挙型に持たせることができます。

インスタンスメソッド

メソッドの定義は基本的には関数の定義と同じです。型の定義の中に、funcをつけて記述します。


/* 職種 */
enum Job {
    case Warrior, Mage, Thief, Priest
    func initialHitPoint() -> Int {
        switch self {
        case .Warrior:  // 戦士
            return 100
        case .Mage:     // 魔法使い
            return 40
        case .Thief:    // 盗賊
            return 60
        case .Priest:   // 僧侶
            return 30
        }
    }
}

/* キャラクタ */
class GameCharacter {
    var name: String
    var job: Job
    var maxHitPoint: Int {
        didSet {
            self.hitPoint = self.maxHitPoint
        }
    }
    var hitPoint: Int = 0
    // イニシャライザ
    init(name: String, job: Job) {
        self.name = name
        self.job = job
        self.maxHitPoint = self.job.initialHitPoint()
    }
    // ダメージを与える
    func addDamage(damage: Int) {
        hitPoint -= damage
        if hitPoint <= 0 {
            print("死んだ")
        }
    }

}

インスタンスメソッドを呼び出す場合は、フロパティと同様、.(ドット)をつけて呼び出します。


let player = GameCharacter(name: "ヨシヒコ", job: .Warrior)
print(player.hitPoint)   // 100
player.addDamage(40)
print(player.hitPoint)   // 60

引数名

メソッドの引数名も関数の場合と同様、外部名と内部名を指定できます。但し、メソッドの場合は、2つ目の引数以降は特に明示的に指定しない限りデフォルトで内部名が外部名扱いになります。
上の例で、addDamageメソッドに第2引数をつけて次の様にしたとすると、呼び出し時に第2引数のラベルが必要になります。


    :
    // ダメージを与える
    func addDamage(damage: Int, times: Int) {
        hitPoint -= damage * times
        if hitPoint < 0 {
            print("死んだ")
        }
    }
}
let player = GameCharacter(name: "ヨシヒコ", job: .Warrior)
player.addDamage(40, times: 3)
// 死んだ

第1引数にも外部名をつけたい場合は、関数の場合と同様、明示的に書くか、内部名の前に#をつけます。


    :
    // ダメージを与える
    func addDamage(#damage: Int, times: Int) {
        hitPoint -= damage * times
        if hitPoint < 0 {
            print("死んだ")
        }
    }
}
:
player.addDamage(damage: 40, times: 3)

第2引数以降のラベルを省略したい場合(推奨されませんが)は、外部名として、_(下線)を指定します。これも関数の場合と同様です。


    :
    // ダメージを与える
    func addDamage(damage: Int, _ times: Int) {
        hitPoint -= damage * times
        if hitPoint < 0 {
            print("死んだ")
        }
    }
}
:
player.addDamage(40, 3)
Swiftのメソッドの第2引数以降のラベルがデフォルトで必要になっているのはObjective-Cの関数の書き方とよく似ています。例えば、UIKitのテーブルビューのセルを返すメソッドがUITalbeViewDataSourceプロトコルにありますが、このメソッドは、Objective-Cでは次の様に定義されています。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

// メソッドの呼び出し
UITableViewCell *cell = [dataSource tableView:tableView cellForRowAtIndexPath:indexPath];

このメソッドのSwift版は、次のようになります。


func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!

// メソッドの呼び出し
let cell = dataSource.tableView(tableView, cellForRowAtIndexPath: indexPath)

このように、メソッド呼び出しの書き方がObjective-CとSwiftで似たようなスタイルになるので、Objective-Cからの移行がしやすくなっています。また、このスタイルに合わせるためには、関数名の最後が第一引数の意味と合うように命名する必要があります。


// 例
func replaceAtIndex:(index: Int, withData: MyData)
func hitBy:(monster: Monster, atLocationX: Int, andY: Int)

破壊的メソッド

構造体、列挙型のメソッドは、デフォルトでは属性の変更はできません。クラスの場合は可能です。


/* 職種 */
enum Job {
    case Warrior, Mage, Thief, Priest
}

/* ゲームキャラクタ */
struct GameCharacter {
    var name: String        // 名前
    var job: Job            // 職種
    var level: Int          // レベル
    func changeJob(newJob: Job) {
        self.job = newJob   // コンバイルエラー
    }
}

構造体や列挙型のメソッドで値を変更するには、funcの前に、mutatingをつけて宣言する必要があります。


/* ゲームキャラクタ */
struct GameCharacter {
    var name: String        // 名前
    var job: Job            // 職種
    var level: Int          // レベル
    mutating func changeJob(newJob: Job) {
        self.job = newJob   //  OK
    }
}

また、定数の構造体や列挙型の破壊的メソッドを呼び出す事はできません。変数であれば可能です。


let player1 = GameCharacter(name: "ヨシヒコ", job: .Warrior, level: 10)
player.changeJob(.Mage)     // コンパイルエラー

var player2 = GameCharacter(name: "ダンジョー", job: .Warrior, level: 10) 
player2.changeJob(.Mage)     // OK

破壊的メソッドでは、次のようにself自体を変更することも可能です。


/* ゲームキャラクタ */
struct GameCharacter {
    :
    mutating func changeJob(newJob: Job) {
        self = GameCharacter(name: self.name, job: newJob, level: self.level)
    }
}

var player = GameCharacter(name: "ヨシヒコ", job: .Warrior, level: 10)
player.changeJob(.Mage)

print(player.job == .Mage)    // true

/* オセロの駒 */
enum OthelloPiece {
    case White, Black
    mutating func reverse() {
        self = (self == .White ? .Black : .White)
    }
}

var piece = OthelloPiece.White
piece.reverse();
print(piece == .Black)    // true

クラスのメソッドにmutatingは不要です。(つけるとエラーになる。)クラスの場合は最初からメソッド内でプロパティの変更が可能です。これは型が値型(変数や定数に値全体のコピーが代入される。IntやString等のプリミティブな型も値型)か、参照型(変数や定数に、インスタンスのアドレスが代入される)かの違いによるものです。構造体と列挙型は値型で、クラスは参照型です。

型メソッド

型メソッドは、クラスや構造体のインスタンスではなく、型自体に属するメソッドです。他の言語ではクラスメソッドや静的メンバ関数と呼んだりします。
型プロパティと同様、構造体と列挙型の場合はstatic、クラスの場合は、class をつけて定義します。呼び出す時は、型名.メソッド名の形で呼び出します。


/* じゃんけん */
enum Janken {
    case goo, choki, paa
    static func sayPriming() {
        print("最初はグー")
    }
}
Janken.sayPriming()     // 最初はグー

型メソッド内で、型プロパティに直接アクセスできます。プロパティの前に型名をつける必要はありません。


/* ログイン情報 */
struct LoginInfo {
    static var url = "https://login.example.com/"
    static func isSecure() -> Bool {
        return url.hasPrefix("https:")
    }
    
    var userid: String
    var password: String
}

print(LoginInfo.isSecure())   // true

/* パーソンクラス */
class Person {
    class var tableName: String {
        return "people"     // データペーステーブル名
    }
    class func tableNameWithPrefix(prefix: String) -> String {
        return "\(prefix)_\(tableName)"
    }
    
    var name: String
    var address: String
    var tel: String
    var email: String
    init(name: String, address: String, tel: String, email: String) {
        self.name = name
        self.address = address
        self.tel = tel
        self.email = email
    }
}

print(Person.tableNameWithPrefix("bak"))  // bak_people