Swiftでメソッドの命名時に引数のルールに迷ったら見直すメモ

  • 68
    いいね
  • 0
    コメント

注意: この記事はSwift1の時代のものであり、仕様が変わっています。都度変更したいと思っていますが、間違いもあります。

Swiftのメソッドの引数の定義は実は複雑ですぐにわけが分からなくなるのでまとめようと思った。

前提として用語は次の通り。

  • ローカル引数名(Local Parameter Names): メソッドもしくは関数内部で利用する引数名
  • 外部引数名(External Parameter Names): メソッドもしくは関数呼び出し側からも利用する引数名。引数の説明になっている。
  • Hash Symbol(#) : ローカル引数名と外部引数名が同じ場合に使う
    • Swift2からこの仕様は変更になり#は使えなくなった
  • Underscore(_) : 外部引数名を省略する場合に使う

次に具体例を示す。

具体例

外部引数名を指定しない例(インスタンスメソッド)

class Hoge {
    func instanceMethod1(s1: String, s2: String) {

    }
}

呼び出し方

swiftで
let word = hoge.instanceMethod1("hello", s2: "world")
objcで
String *word = [hoge instanceMethod1:@"hello"
                                  s2:@"world"];

引数の2つ目以降、外部引数名が指定されていないメソッドはそのままローカル引数名であるs2が外部に晒される。

同様のメソッドをObjective-Cから呼び出す場合も同じ。

外部引数名を指定する例

引数の2つ目s2にtoStringという外部引数名を、3つ目の引数joinerにwithJoinerという外部引数名を付けた。

class Hoge {
    func join(string s1: String, toString s2: String, withJoiner joiner: String) -> String {
        return s1 + joiner + s2
    }
}

呼び出し方

swiftで
let word = hoge.join(string: "hello", toString: "world", withJoiner: ", ")
objcで
String *word = [hoge joinWithString:@"hello",
                            toString:@"world"
                          withJoiner:@","];

この例では外部引数名stringやtoStringのおかげで役割が明確になった。

また、Objective-Cから呼び出す場合、最初の引数にはWithが自動で付けられる。

外部引数名とローカル引数名が同じ時はHash Symbol(#)を付ける

外部引数名とローカル引数名が同じものを付けたい場合、#を付ける。付けないとコンパイラが警告を出すので気づく。

class Hoge {
    // func​ ​containsCharacter​(string string​: ​String​, ということをしたい
    func containsCharacter(#string: String, #characterToFind: Character) -> Bool {}
}

呼び出し方

swiftで
let containsAVee = hoge.containsCharacter(string: "aardvark", characterToFind: "v")
objcで
BOOL containsAVee = [hoge containsCharacterWithString:@"aardvark" 
                                       characterToFind: @"v"];

特に違和感なし

外部引数名を_で省略する

class Hoge {
    func containsCharacter(string: String, _ characterToFind: Character) -> Bool {}
}

呼び出し方

swiftで
hoge.containsCharacter("abc", "b")
objcで
[hoge containsCharacter:"abc" :"b"];

余談として、メソッドの第一引数に対して外部引数名を省略すると下記のような警告が出る(そもそも第一引数は外部引数名が必要なくても良いので意味ないことをしてみた)。

Extraneous _ in parameter: 'string' has no keyword argument name

ちなみに、メソッドではなくinitで_を利用した場合は警告はない。

イニシャライザ

ついでとしてイニシャライザでの外部引数名の話をしよう

class Age {
    // メソッドと違い第一引数も外部引数名になる
    init(age: Double) {

    }
    // 省略したい
    init(_ age: Double) {

    }
}

呼び出し方

swiftで
let hoge1 = Age(age: 2.0)
let hoge2 = Age(2.0)     //_で省略。意図は通じる
objcで
Age *hoge1 = [[Age alloc] initWithAge:2.0];
Age *hoge2 = [[Age alloc] init:2.0]; //_で省略したが分かりづらいしObjective-C的にはとても不自然

イニシャライザの場合は第一引数のローカル引数名を外部引数名としてしまうため、それを省略したい場合には_を使って省略できる。

イニシャライザの外部引数名について考察

説明のため極端な例を出したが、このようなAgeクラスにはDoubleでageを指定するんだから説明不要だろ、みたいな極端な場合に利用するのが良い気がする。逆に言えば 「イニシャライザでは外部引数名は省略されたくない。だからローカル引数名を第一引数から自動的に外部引数名にしている」と考えると納得できるだろう。

その考えは実際にコードを書いていると良い制約になりプログラマフレンドリーな側面があるが、プログラミング初学者がコードに触れる前から覚えないといけない仕様としてはフレンドリーではない。

まとめ

  • メソッドの第一引数の外部引数名は省略される
    • 指定したければ指定する
    • #をつければローカル引数名が外部引数名になる
    • _を付ける意味はないし警告が出る
  • メソッドの第二引数以降のローカル引数名が自動的に外部引数名になる
    • 自分でローカル引数名とは別のものを指定したければ指定する
    • ローカルと同じでよければそのまま
    • 省略したければ_を付ける
  • イニシャライザはローカル引数名全てが自動的に外部引数名になる
    • 省略したければ_を付ける

開発メンバーと認識の合うという意味での良いメソッド名にするにはObjective-Cを意識してメソッド名を定義しても良いかもしれない。

注意

メソッド中心にまとめたが関数の場合は微妙にルールが違う様子。あまりメソッドでないむき出しの関数使わないからどうでも良かった。

間違っている点もあるかもしれないのでコメントか編集リクエストください。

参考

Apple Inc. “The Swift Programming Language”。 iBooks.
https://itun.es/jp/jEUH0.l

関数,メソッド,イニシャライザで引数の外部名の作法が全部違う件(関数編)
http://enamelsystems.com/0020/