Quando sono richieste le etichette argomento in Swift?

Rispondendo a questa domanda, si è arrivati ​​a quella discussione che le etichette erano necessarie per una chiamata a init . Questo è normale in Swift.

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

Tuttavia, le forze straniere sono in gioco:

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

Per usare un’etichetta argomento qui dovrebbe essere dichiarata esplicitamente.

Non ho visto qualcosa di molto approfondito che spiegasse tutto questo nella documentazione. Per quali varietà di classi / istanze / funzioni globali sono necessarie etichette argomento? I metodi Obj-C vengono sempre esportati e importati con etichette di argomenti?

A partire da Swift 3.0 questo è cambiato di nuovo: tutti i metodi, le funzioni e gli inizializzatori richiedono etichette argomento per tutti i parametri, a meno che tu non abbia esplicitamente scelto di utilizzare il nome esterno _ . Ciò significa che metodi come addChildViewController(_:) ora sono scritti in questo modo:

 func addChildViewController(_ childController: UIViewController) 

Questo è stato proposto e approvato come parte del processo Swift Evolution ed è stato implementato in SR-961 .

Tutti i metodi init richiedono nomi di parametri:

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

Tutti i metodi richiamati su un object utilizzano nomi di parametri per tutto tranne il primo parametro:

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

Tutte le funzioni di class incluse in Swift e objective-c seguono lo stesso schema. Puoi anche aggiungere esplicitamente nomi esterni.

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

Le funzioni Swift che non sono una funzione di class non richiedono nomi di parametri, ma è comunque ansible aggiungerle esplicitamente:

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

Come ha detto Bryan, è necessario che le chiamate al metodo Swift abbiano senso quando vengono richiamati i metodi objective-c che hanno nomi di parametri nella firma del metodo. I metodi Init includono il primo parametro perché Swift cambia i metodi init da objective-c da initWith: … a Class () in modo che il nome del primo parametro non sia più incluso nel nome del metodo.

Swift 3.0

In Swift 3.0, previsto per la fine del 2016 , il comportamento predefinito è semplice:

  • Tutti i parametri per tutti i metodi hanno etichette esterne per impostazione predefinita.

Puoi trovare queste regole in modo più conciso nelle linee guida per la progettazione dell’API Swift . Questo nuovo comportamento è stato proposto in SE-0056, “stabilisce un comportamento coerente delle etichette su tutti i parametri, comprese le prime etichette” e implementato in SR-961 . Il comportamento predefinito può essere modificato come descritto di seguito, in “Override the Default Behavior”.

Swift 2.2

In Swift 2.2, le impostazioni predefinite della lingua per la presenza di etichette di argomenti esterni sono cambiate e sono ora più semplici. Il comportamento predefinito può essere riepilogato come segue:

  • I primi parametri di metodi e funzioni non dovrebbero avere etichette di argomenti esterni.
  • Altri parametri di metodi e funzioni dovrebbero avere etichette di argomenti esterni.
  • Tutti i parametri per gli inizializzatori devono avere etichette di argomento esterne.

Il comportamento predefinito può essere modificato come descritto di seguito, in “Override the Default Behavior”.

Un esempio

Queste regole sono meglio dimostrate con un esempio:

 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 

Override del comportamento predefinito

Per qualsiasi parametro di qualsiasi metodo o funzione, è ansible deviare dall’impostazione predefinita della lingua, benché la guida di stile ti avvisa correttamente di non farlo, a meno che non ci sia una buona ragione.

Per aggiungere un’etichetta di parametri esterni dove normalmente non ce ne sarebbe uno solo applicabile in Swift 2.2, poiché Swift 3.0 imposta automaticamente l’assegnazione di etichette esterne a ogni parametro – o per modificare un’etichetta di parametro esterna – applicabile a entrambe le versioni – scrivere il parametro esterno desiderato etichetta prima dell’etichetta del parametro locale:

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

Per rimuovere un’etichetta di parametro esterna dove normalmente ce ne sarebbe una, utilizzare l’etichetta di parametro esterna speciale _ :

 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) 

Queste “sostituzioni predefinite” funzioneranno per qualsiasi metodo o funzione, inclusi gli inizializzatori.

Ecco cosa sono riuscito a raccogliere leggendo la documentazione (abbastanza scarsa) e attraverso la semplice sperimentazione:

  • I metodi Init hanno sempre bisogno delle loro etichette . Iniziamo metodi come etichette, in quanto chiariscono quale metodo init, esattamente, vuoi chiamare. Altrimenti, questo:

     FooBar(foos: 5) 

    E questo:

     FooBar(bars: 5) 

    Sarebbe esattamente lo stesso:

     FooBar(5) 

    In nessun altro caso, i metodi init sono l’unico posto in Swift in cui hanno tutti lo stesso nome, ma argomenti potenzialmente diversi. Ed è per questo…

  • Funzioni , metodi, ecc. (Tutto ciò che non è un metodo init) hanno la prima omissione – questo è per lo stile e per ridurre la ripetitività noiosa. Invece di

     aDictionary.removeValueForKey(key: "four") 

    Abbiamo questo:

     aDictionary.removeValueForKey("four") 

    E hanno ancora argomenti abbastanza ambigui e di facile lettura per le funzioni con due parametri. Quindi invece di

     anArray.insert("zebras", 9) 

    Abbiamo un modulo molto più comprensibile in lettura:

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

Quale sembra molto meglio. Quando ero al WWDC, questo è stato pubblicizzato come una caratteristica di Swift: argomenti moderni e brevi in ​​stile Java, senza sacrificare la leggibilità. Ciò facilita anche la transizione da Objective-C, come mostra la risposta di Bryan Chen .

È ansible creare un’etichetta parametro necessaria per chiamare un metodo utilizzando # prima dell’etichetta.

Per esempio:

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

Può essere migliorato in questo modo:

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

(Dal WWDC 2014 – 416 – Building Modern Frameworks , 15 minuti in)

È solo rendere i metodi ObjC belli in Swift.

Documentazione

Metodi di istanza

Nomi dei parametri locali ed esterni per i metodi

In particolare, Swift fornisce il nome del primo parametro in un metodo un nome di parametro locale per impostazione predefinita e fornisce per default il secondo nome e il parametro successivo nomi di parametri locali ed esterni. Questa convenzione corrisponde alle convenzioni tipiche di denominazione e chiamata che conoscerete dalla scrittura dei metodi Objective-C e consente chiamate di metodo espressive senza la necessità di qualificare i nomi dei parametri.

Il comportamento predefinito sopra descritto significa che le definizioni dei metodi in Swift sono scritte con lo stesso stile grammaticale di Objective-C e sono chiamate in modo naturale ed espressivo.

Personalizzazione dell’inizializzazione

Nomi dei parametri locali ed esterni

Tuttavia, gli inizializzatori non hanno un nome di funzione identificativo prima delle parentesi nel modo in cui funzionano le funzioni e i metodi. Pertanto, i nomi e i tipi dei parametri di un inizializzatore svolgono un ruolo particolarmente importante nell’identificazione di quale inizializzatore deve essere chiamato. Per questo motivo, Swift fornisce un nome esterno automatico per ogni parametro in un inizializzatore se non si fornisce un nome esterno.

Ad esempio per questa class ObjC

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

e ha scritto in Swift

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

chiamare la versione ObjC

 [counter incrementBy:10 numberOfTimes:2]; 

e versione Swift

 counter.incrementBy(10, numberOfTimes:2) 

puoi vedere che sono quasi la stessa cosa