web-dev-qa-db-ja.com

Swiftで引数ラベルはいつ必要ですか?

この質問 の回答では、initの呼び出しに引数ラベルが必要であることがわかりました。これはSwiftでは正常です。

class Foo {
    init(one: Int, two: String) { }
}

let foo = Foo(42, "Hello world") // Missing argument labels 'one:two:' in call

ただし、見知らぬ部隊が活躍しています。

extension Foo {
    func run(one: String, two: [Int]) { }
}

foo.run(one: "Goodbye", two: []) // Extraneous argument label 'one:' in call

ここで引数ラベルを使用するには、明示的に宣言する必要があります。

ドキュメントでこれをすべて説明しているものはあまり見ていません。引数ラベルはどの種類のクラス/インスタンス/グローバル関数に必要ですか? Obj-Cメソッドは常に引数ラベル付きでエクスポートおよびインポートされますか?

62
jtbandes

Swift 3.0のように、これは再び変更されました。すべてのメソッド、関数、および初期化子は、allパラメーターの引数ラベルを必要とします。外部名___を使用して明示的にオプトアウトしていない限り、これはaddChildViewController(_:)などのメソッドが次のように記述されることを意味します。

_func addChildViewController(_ childController: UIViewController)
_

これは 提案および承認済みSwift Evolutionプロセス の一部であり、 SR-961 で実装されました。

28
TwoStraws

すべてのinitメソッドにはパラメーター名が必要です。

var view = NSView(frame: NSRect(x: 10, y: 10, width: 50, height: 50))
class Foo {
    init(one: Int, two: String) { }
}

let foo = Foo(one: 42, two: "Hello world")

オブジェクトで呼び出されるすべてのメソッドは、最初のパラメーターを除くすべてのパラメーター名を使用します。

extension Foo {
    func run(one: String, two: [Int]) { }
}
foo.run("Goodbye", two: [])

SwiftおよびObjective-cのクラス関数を含むすべてが同じパターンに従います。外部名を明示的に追加することもできます。

extension Foo{
class func baz(one: Int, two: String){}
class func other(exOne one: Int,  exTwo two: String){}
}
Foo.baz(10, two:"str")
Foo.other(exOne: 20, exTwo:"str")

クラス関数ではないSwift関数はパラメーター名を必要としませんが、それでも明示的に追加できます。

func bar(one: Int, two: String){}
bar(1, "hello")

ブライアンが言ったように、Swiftメソッド呼び出しは、メソッドシグネチャにパラメーター名を持つObjective-Cメソッドで呼び出されたときに意味があります。Swiftは、initメソッドをObjective-CからinitWith:...からClass()に変更し、最初のパラメーター名がメソッド名に含まれないようにします。

43
Connor

Swift 3.0

Swift 3.0、 2016年後半にリリース予定 )では、デフォルトの動作は単純です:

  • すべてのメソッドのすべてのパラメーターには、デフォルトで外部ラベルがあります。

Swift API Design Guidelines でこれらのルールを最も簡潔に見つけることができます。この最新の動作は SE-0056、「最初のラベルを含むすべてのパラメーターで一貫したラベル動作を確立する」 で提案され、 SR-961 で実装されました。デフォルトの動作は、以下の「デフォルトの動作のオーバーライド」で説明されているように変更できます。

スイフト2.2

Swift 2.2では、外部引数ラベルの存在に対する言語のデフォルトが変更され、より簡単になりました。デフォルトの動作は次のように要約できます。

  • メソッドと関数の最初のパラメーターには、外部引数ラベルを使用しないでください。
  • メソッドおよび関数の他のパラメーターには、外部引数ラベルが必要です。
  • イニシャライザへのすべてのパラメータには、外部引数ラベルが必要です。

デフォルトの動作は、以下の「デフォルトの動作のオーバーライド」で説明されているように変更できます。

これらの規則は、例を使用して最もよく実証されます。

func printAnimal(animal: String, legCount: Int) {
    let legNoun = legCount == 1 ? "leg" : "legs"
    print("\(animal) has \(legCount) \(legNoun)")
}

struct Player {
    let name: String
    let lives: Int

    init(name: String, lives: Int) {
        self.name = name
        self.lives = lives
    }

    func printCurrentScore(currentScore: Int, highScore: Int) {
        print("\(name)'s score is \(currentScore). Their high score is \(highScore)")
    }
}


// Swift 3.0
// In Swift 3.0, all argument labels must be included
printAnimal(animal: "Dog", legCount: 4)
let p = Player(name: "Riley", lives: 3)
p.printCurrentScore(currentScore: 50, highScore: 110)

// Swift 2.2
// In Swift 2.2, argument labels must be included or omitted in exactly the following way
// given the definition of the various objects.
printAnimal("Dog", legCount: 4)
let p = Player(name: "Riley", lives: 3)
p.printCurrentScore(50, highScore: 110)

// In Swift 2.2, none of the following will work
printAnimal(animal: "Dog", legCount: 4)  // Extraneous argument label 'animal:' in call
let q = Player("Riley", lives: 3)  // Missing argument label 'name:' in call
p.printCurrentScore(50, 110)  // Missing argument label 'highScore:' in call

デフォルトの動作をオーバーライドする

メソッドや関数のパラメーターについては、言語のデフォルトから逸脱する可能性がありますが、正当な理由がない限り、スタイルガイドはそうしないように警告しています。

通常は存在しない外部パラメーターラベルを追加するには、Swift 2.2でのみ適用可能、Swift 3.0ではデフォルトですべてのパラメーターに外部ラベルが割り当てられます–または、両方のバージョンに適用可能な外部パラメーターラベルを変更するには、ローカルパラメーターラベルの前に目的の外部パラメーターラベルを書き込みます。

func printAnimal(theAnimal animal: String, legCount: Int) {
    let legNoun = legCount == 1 ? "leg" : "legs"
    print("\(animal) has \(legCount) \(legNoun)")
}

printAnimal(theAnimal: "Dog", legCount: 4)

通常存在する外部パラメーターラベルを削除するには、特別な外部パラメーターラベル_を使用します。

func printAnimal(animal: String, _ legCount: Int) {
    let legNoun = legCount == 1 ? "leg" : "legs"
    print("\(animal) has \(legCount) \(legNoun)")
}

// Swift 3.0
printAnimal(theAnimal: "Dog", 4)

// Swift 2.2
printAnimal("Dog", 4)

これらの「デフォルトのオーバーライド」は、初期化子を含むすべてのメソッドまたは関数に対して機能します。

30
ravron

ここに、(かなりまばらな)ドキュメントを読んで、そして単純な実験を通して集めたものがあります:

  • Initメソッドは常にラベルが必要です。ラベルのような初期化メソッドは、どの初期化メソッドを正確に呼び出すかを明確にするためです。それ以外の場合、これ:

    FooBar(foos: 5)
    

    この:

    FooBar(bars: 5)
    

    まったく同じに見えるでしょう:

    FooBar(5)
    

    これは他のどこにも当てはまりません-initメソッドはSwiftで唯一同じ場所ですが、引数が異なる可能性があります。そのため...

  • 関数、メソッドなど(がinitメソッドではないもの)は最初のラベルは省略されました-これはスタイルのためであり、退屈な繰り返し性を減らすためです。の代わりに

    aDictionary.removeValueForKey(key: "four")
    

    これがあります:

    aDictionary.removeValueForKey("four")
    

    また、twoパラメーターを持つ関数へのかなり曖昧で読みやすい引数がまだあります。の代わりに

    anArray.insert("zebras", 9)
    

    もっと読みやすいフォームがあります:

    anArray.insert("zebras", atIndex: 9)
    

それはずっと良く見えます。私がWWDCにいたとき、これはSwiftの機能であると宣伝されました。読みやすさを犠牲にすることなく、Javaスタイルのモダンで短い議論です。 Bryan Chenの答え が示すように、これによりObjective-Cからの移行も容易になります。

11
Undo

ラベルの前に#を使用して、メソッドの呼び出しに必要なパラメーターラベルを作成できます。

例えば。:

func addLocation(latitude : Double, longitude : Double) { /*...*/ }
addLocation(125.0, -34.1) // Not clear

次のように改善できます:

func addLocation(#latitude : Double, #longitude : Double) { /*...*/ }
addLocation(latitude: 125.0, longitude: -34.1) // Better

WWDC 2014-416-モダンなフレームワークの構築 、15分後)

8
Robert

ObjCメソッドをSwiftで見栄えよくするだけです。

ドキュメント

インスタンスメソッド

メソッドのローカルおよび外部パラメーター名

特に、Swiftは、メソッドのfirst parameter nameにデフォルトでlocal parameter nameを与え、デフォルトでは、ローカルと外部の両方のパラメーター名です。この規則は、Objective-Cメソッドの記述でよく知っている一般的な命名規則と呼び出し規則に一致し、パラメーター名を修飾する必要のない表現力豊かなメソッド呼び出しを実現します。

...

上記のデフォルトの動作では、Swiftのメソッド定義はObjective-Cと同じ文法スタイルで記述され、自然で表現力豊かな方法で呼び出されます。

初期化のカスタマイズ

ローカルおよび外部パラメーター名

ただし、初期化子には、関数とメソッドのように、括弧の前に識別関数名がありません。したがって、初期化子のパラメーターの名前とタイプは、どの初期化子を呼び出すべきかを識別する際に特に重要な役割を果たします。このため、自分で外部名を指定しない場合、Swiftは初期化子のすべてのパラメーターに自動外部名を提供します

このObjCクラスの例

@interface Counter : NSObject

@property int count;

- (void)incrementBy:(int)amount numberOfTimes:(int)numberOfTimes;

@end

そして、それはスウィフトで書かれました

class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, numberOfTimes: Int) {
        count += amount * numberOfTimes
    }
}

objCバージョンを呼び出す

[counter incrementBy:10 numberOfTimes:2];

およびSwift=バージョン

counter.incrementBy(10, numberOfTimes:2)

ほぼ同じであることがわかります

4
Bryan Chen