Quando os labels de argumentos são necessários no Swift?

Ao responder a essa pergunta , surgiu que os labels de argumentos eram necessários para uma chamada ao init . Isso é normal no Swift.

 class Foo { init(one: Int, two: String) { } } let foo = Foo(42, "Hello world") // Missing argument labels 'one:two:' in call 

No entanto, forças estranhas estão em jogo:

 extension Foo { func run(one: String, two: [Int]) { } } foo.run(one: "Goodbye", two: []) // Extraneous argument label 'one:' in call 

Para usar um label de argumento aqui, ele teria que ser declarado explicitamente.

Eu não vi algo muito completo explicando tudo isso na documentação. Para quais variedades de classs / instâncias / funções globais são necessárias as etiquetas de argumentos? Os methods Obj-C são sempre exportados e importados com labels de argumentos?

A partir do Swift 3.0, isso mudou novamente: todos os methods, funções e inicializadores exigem labels de argumentos para todos os parâmetros, a menos que você tenha optado explicitamente por usar o nome externo _ . Isso significa que methods como addChildViewController(_:) agora são escritos assim:

 func addChildViewController(_ childController: UIViewController) 

Isso foi proposto e aprovado como parte do processo Swift Evolution e foi implementado no SR-961 .

Todos os methods init exigem nomes de parâmetros:

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

Todos os methods chamados em um object usam nomes de parâmetros para tudo, exceto o primeiro parâmetro:

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

Todas as funções de class incluindo em Swift e object-c seguem o mesmo padrão. Você também pode adicionar explicitamente nomes externos.

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

Funções Swift que não são uma function de class não requerem nomes de parâmetros, mas você ainda pode explicitamente adicioná-las:

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

Como Bryan disse, é para fazer chamadas de método Swift fazer sentido quando chamado em methods object-c que possuem nomes de parâmetros na assinatura do método. Os methods de boot incluem o primeiro parâmetro, pois o Swift altera os methods init de objective-c de initWith: … para Class (), portanto, o nome do primeiro parâmetro não é mais incluído no nome do método.

Swift 3.0

No Swift 3.0, previsto para ser lançado no final de 2016 , o comportamento padrão é simples:

  • Todos os parâmetros para todos os methods possuem labels externos por padrão.

Você pode encontrar essas regras de maneira mais concisa nas Diretrizes de design da API do Swift . Esse comportamento mais recente foi proposto em SE-0056, “estabelecer um comportamento de label consistente em todos os parâmetros, incluindo os primeiros labels”, e implementado no SR-961 . O comportamento padrão pode ser alterado conforme descrito abaixo, em “Anulando o comportamento padrão”.

Swift 2.2

No Swift 2.2, os padrões da linguagem para a presença de labels de argumentos externos mudaram e agora são mais simples. O comportamento padrão pode ser resumido da seguinte forma:

  • Os primeiros parâmetros para methods e funções não devem ter labels de argumentos externos.
  • Outros parâmetros para methods e funções devem ter labels de argumentos externos.
  • Todos os parâmetros para inicializadores devem ter labels de argumentos externos.

O comportamento padrão pode ser alterado conforme descrito abaixo, em “Anulando o comportamento padrão”.

Um exemplo

Essas regras são melhor demonstradas com um exemplo:

 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 

Substituindo o comportamento padrão

Para qualquer parâmetro para qualquer método ou function, você pode se desviar do padrão do idioma, embora o guia de estilo o avise corretamente, a menos que exista um bom motivo.

Para adicionar um label de parâmetro externo onde normalmente não haveria um – aplicável apenas no Swift 2.2, já que o Swift 3.0 padroniza a atribuição de labels externos a cada parâmetro – ou a alteração de um label de parâmetro externo – aplicável a ambas as versões – grava o parâmetro externo desejado label antes do label do parâmetro local:

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

Para remover um label de parâmetro externo onde normalmente haveria um, use o label de parâmetro externo especial _ :

 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) 

Essas “substituições padrão” funcionarão para qualquer método ou function, incluindo inicializadores.

Aqui está o que eu consegui reunir lendo a documentação (bastante escassa), e através de experimentação simples:

  • Os methods de boot sempre precisam de seus labels . Métodos de boot como labels, como eles deixam claro qual método init, exatamente, você deseja chamar. Caso contrário, isso:

     FooBar(foos: 5) 

    E isto:

     FooBar(bars: 5) 

    Ficaria exatamente o mesmo:

     FooBar(5) 

    Em nenhum outro lugar é esse o caso – os methods init são o único lugar no Swift onde todos eles têm o mesmo nome, mas potencialmente argumentos diferentes. E é por isso…

  • Funções , methods, etc (qualquer coisa que não seja um método init) tem o primeiro label omitido – isso é para estilo e para reduzir a repetitividade chata. Ao invés de

     aDictionary.removeValueForKey(key: "four") 

    Nós temos isso:

     aDictionary.removeValueForKey("four") 

    E ainda tem argumentos razoavelmente não ambíguos e fáceis de ler para funções com dois parâmetros. Então, ao invés de

     anArray.insert("zebras", 9) 

    Nós temos uma forma de leitura muito mais compreensível:

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

Que parece muito melhor. Quando eu estava na WWDC, isso foi apresentado como uma característica do Swift: argumentos curtos e modernos, estilo Java, sem sacrificar a legibilidade. Isso também facilita a transição do Objective-C, como mostra a resposta de Bryan Chen .

Você pode fazer um label de parâmetro necessário para chamar um método usando # antes do label.

Por exemplo:

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

Pode ser melhorado assim:

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

(De WWDC 2014 – 416 – Building Modern Frameworks , 15 mins in)

É só fazer com que os methods ObjC pareçam agradáveis ​​no Swift.

Documentação

Métodos de instância

Nomes de parameters Locais e Externos para Métodos

Especificamente, o Swift fornece o primeiro nome de parâmetro em um método, por padrão, um nome de parâmetro local e, por padrão, fornece ao segundo e aos nomes de parâmetros subsequentes nomes de parâmetros locais e externos. Essa convenção corresponde à típica convenção de nomenclatura e chamada com a qual você estará familiarizado ao escrever methods Objective-C e faz chamadas de método expressivas sem a necessidade de qualificar seus nomes de parâmetro.

O comportamento padrão descrito acima significa que as definições de método no Swift são escritas com o mesmo estilo gramatical que o Objective-C e são chamadas de maneira natural e expressiva.

Customizando a boot

Nomes de parameters Locais e Externos

No entanto, os inicializadores não têm um nome de function de identificação antes de seus parênteses da maneira que as funções e os methods fazem. Portanto, os nomes e tipos de parâmetros de um inicializador desempenham um papel particularmente importante na identificação de qual inicializador deve ser chamado. Por causa disso, o Swift fornece um nome externo automático para cada parâmetro em um inicializador, se você não fornecer um nome externo por conta própria.

Por exemplo, para esta class ObjC

 @interface Counter : NSObject @property int count; - (void)incrementBy:(int)amount numberOfTimes:(int)numberOfTimes; @end 

e escrito em Swift

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

para chamar a versão de ObjC

 [counter incrementBy:10 numberOfTimes:2]; 

e versão Swift

 counter.incrementBy(10, numberOfTimes:2) 

você pode ver que eles são quase os mesmos