Когда в 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 экспортируются и импортируются с метками аргументов?

Ответ 1

С Swift 3.0 это снова изменилось: всем методам, функциям и инициализаторам требуются метки аргументов для параметров all, если вы явно не выбрали внешнее имя _. Это означает, что теперь такие методы, как addChildViewController(_:), записываются так:

func addChildViewController(_ childController: UIViewController)

Это было предложено и одобрено в рамках процесса Swift Evolution и было реализовано в SR-961.

Ответ 2

Для всех методов 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, имеющих имена параметров в сигнатуре метода. Методы Init включают первый параметр, поскольку Swift меняет методы init из objective-c из initWith:... в Class(), поэтому имя первого параметра больше не включено в имя метода.

Ответ 3

Swift 3.0

В Swift 3.0, который будет выпущен в конце 2016 года, поведение по умолчанию прост:

  • По умолчанию все параметры для всех методов имеют внешние метки.

Вы можете найти эти правила наиболее четко в Руководстве по дизайну Swift API. Это новое поведение было предложено в SE-0056, "установить согласованное поведение меток по всем параметрам, включая первые метки" , и реализовано в SR-961. Поведение по умолчанию может быть изменено, как описано ниже, в разделе "Переопределение поведения по умолчанию" .

Swift 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) 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)

Эти "переопределения по умолчанию" будут работать для любого метода или функции, включая инициализаторы.

Ответ 4

Вот то, что я смог собрать, прочитав (довольно редкую) документацию и используя простые эксперименты:

  • Для методов Init всегда нужны свои метки. Инициируйте методы, такие как метки, поскольку они дают понять, какой метод init, точно, вы хотите вызвать. В противном случае это:

    FooBar(foos: 5)
    

    И это:

    FooBar(bars: 5)
    

    Будет выглядеть точно так же:

    FooBar(5)
    

    Нигде в этом случае - методы init - это единственное место в Swift, где все они имеют одно и то же имя, но потенциально разные аргументы. Вот почему...

  • Функции, методы и т.д. (все, что не является методом init) имеют первый ярлык пропущен - это для стиля и сокращения скучной повторяемости. Вместо

    aDictionary.removeValueForKey(key: "four")
    

    Мы имеем это:

    aDictionary.removeValueForKey("four")
    

    И все еще имеют довольно неоднозначные и легко читаемые аргументы для функций с двумя параметрами. Поэтому вместо

    anArray.insert("zebras", 9)
    

    У нас есть гораздо более понятная форма для чтения:

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

Что выглядит намного лучше. Когда я был на WWDC, это было рекламировано как особенность быстрых: современных, коротких аргументов в стиле Java, не жертвуя удобочитаемостью. Это также облегчает переход от Objective-C, как ответ Брайана Чена.

Ответ 5

Вы можете сделать метку параметра, необходимую для вызова метода, используя # перед меткой.

например:.

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

(From WWDC 2014 - 416 - Создание современных рамок, 15 минут)

Ответ 6

Это только делает методы ObjC хорошими в Swift.

Documentation

Методы экземпляров

Локальные и внешние имена параметров для методов

В частности, Swift дает имя первого параметра в методе a local имя параметра по умолчанию и дает второй и последующий имена параметров как локальные, так и внешние имена параметров по умолчанию. Это соглашение соответствует типичному соглашению об именах и вызовах, которое вы будут знакомы с написанием методов Objective-C и экспресс-вызовы без необходимости определять ваш параметр имена.

...

Поведение по умолчанию, описанное выше, означает, что определения методов в Swift написаны с тем же грамматическим стилем, что и Objective-C, и вызываются естественным, выразительным способом.

Настройка инициализации

Локальные и внешние имена параметров

Однако инициализаторы не имеют имени идентифицирующей функции перед их круглыми скобками в способе, которым выполняют функции и методы. Поэтому имена и типы параметров инициализаторов играют особенно важную роль в определении того, какой инициализатор следует вызывать. Из-за этого Swift предоставляет автоматическое внешнее имя для каждого параметра в инициализаторе, если вы не предоставляете внешнее имя самостоятельно.

Например, для этого класса ObjC

@interface Counter : NSObject

@property int count;

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

@end

и он написан в Swift

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)

вы можете видеть, что они почти одинаковы