Variabili di funzione statiche in Swift


96

Sto cercando di capire come dichiarare una variabile statica con scope solo localmente a una funzione in Swift.

In C, questo potrebbe assomigliare a questo:

int foo() {
    static int timesCalled = 0;
    ++timesCalled;
    return timesCalled;
}

In Objective-C, è fondamentalmente lo stesso:

- (NSInteger)foo {
    static NSInteger timesCalled = 0;
    ++timesCalled;
    return timesCalled;
}

Ma non riesco a fare niente del genere in Swift. Ho provato a dichiarare la variabile nei seguenti modi:

static var timesCalledA = 0
var static timesCalledB = 0
var timesCalledC: static Int = 0
var timesCalledD: Int static = 0

Ma tutto ciò si traduce in errori.

  • Il primo lamenta "Le proprietà statiche possono essere dichiarate solo su un tipo".
  • Il secondo si lamenta "Dichiarazione attesa" (dove si statictrova) e "Modello previsto" (dove si timesCalledBtrova)
  • Il terzo si lamenta "Le istruzioni consecutive su una riga devono essere separate da"; "(nello spazio tra i due punti e static) e" Tipo previsto "(dove si statictrova)
  • Il quarto reclama "Le dichiarazioni consecutive su una riga devono essere separate da ';'" (nello spazio tra Inte static) e "Dichiarazione prevista" (sotto il segno di uguale)

Risposte:


158

Non credo che Swift supporti la variabile statica senza averla collegata a una classe / struttura. Prova a dichiarare una struttura privata con una variabile statica.

func foo() -> Int {
    struct Holder {
        static var timesCalled = 0
    }
    Holder.timesCalled += 1
    return Holder.timesCalled
}

  7> foo()
$R0: Int = 1
  8> foo()
$R1: Int = 2
  9> foo()
$R2: Int = 3

Sì, ho continuato a giocare un po 'e questa è stata fondamentalmente la soluzione davvero goffa che mi è venuta in mente.
nhgrif

17
Sconvolto, ma sono triste che dobbiamo ricorrere a questo.
Tricertops

1
Le proprietà e i metodi del tipo appartengono a un tipo (cioè una Classe, Struct o Enum) e non possono appartenere solo a una funzione. Documentazione Apple sulle proprietà del tipo . @Tricertops. Un altro modo per farlo sarebbe mettere la funzione "foo" in una classe, creare una proprietà di tipo per quella classe e usarla all'interno della funzione.
NSCoder

6
@NSCoder Ma è possibile dichiarare struct Holder {…}all'interno di più funzioni e non entreranno in conflitto. Swift potrebbe supportare static letsenza questo structboilerplate in giro.
Tricertops

1
@Honey mi dispiace ma non riesco a trovare altra risposta più aggiornata?
Bryan Chen

23

Un'altra soluzione

func makeIncrementerClosure() -> () -> Int {
    var timesCalled = 0
    func incrementer() -> Int {
        timesCalled += 1
        return timesCalled
    }
    return incrementer
}

let foo = makeIncrementerClosure()
foo()  // returns 1
foo()  // returns 2

3
questo è un tipico modo javascript per farlo
Bryan Chen

1
Ma se chiamo di nuovo ba (), la funzione interna restituisce 1 alla prima chiamata. Questo è diverso da una variabile statica.
nhgrif

2
Questo viene insegnato anche nei documenti di Apple qui: developer.apple.com/library/ios/documentation/Swift/Conceptual/… Sembra essere la soluzione migliore solo per restare in linea con la "programmazione funzionale", ma ci sono altre soluzioni come bene. Questa dovrebbe essere la risposta accettata però.
datWooWoo

1
Mi dispiace, ma questo è un brutto trucco che aggiunge più complessità allo stesso problema. Qual è il tuo punto? In tal caso preferisco una semplice proprietà di classe. La risposta di @Brian Chen è la più vicina che puoi ottenere. Uso la sua risposta per una soluzione tipo flipflop. Daniel è forse il più conforme alle regole di programmazione Swift di Apple.
AndaluZ

1
Questa soluzione mi è piaciuta particolarmente. Questo è un perfetto esempio dell'utilizzo di una funzione di ordine superiore per ottenere lo stesso risultato di una variabile statica all'interno dell'ambito di una funzione. Le variabili di funzione statica non sono supportate in modo nativo in Swift per buone ragioni. È l'evoluzione naturale della programmazione. Cercare di codificare in modi antiquati richiede hack. A mio parere, l'aggiunta di un tipo di dati nidificato in più rispetto all'utilizzo dell'acquisizione di variabili riduce la leggibilità del codice.
nstein

18

Swift 1.2 con Xcode 6.3 ora supporta statico come previsto. Dalle note di rilascio beta di Xcode 6.3:

I metodi e le proprietà "statici" sono ora consentiti nelle classi (come alias per "class final"). È ora possibile dichiarare proprietà archiviate statiche nelle classi, che hanno una memoria globale e vengono inizializzate pigramente al primo accesso (come le variabili globali). I protocolli ora dichiarano i requisiti di tipo come requisiti "statici" invece di dichiararli come requisiti di "classe". (17198298)

Sembra che le funzioni non possano contenere dichiarazioni statiche (come richiesto in questione). Invece, la dichiarazione deve essere eseguita a livello di classe.

Semplice esempio che mostra una proprietà statica incrementata all'interno di una funzione di classe (nota anche come statica), sebbene una funzione di classe non sia richiesta:

class StaticThing
{
    static var timesCalled = 0

    class func doSomething()
    {
        timesCalled++

        println(timesCalled)
    }
}

StaticThing.doSomething()
StaticThing.doSomething()
StaticThing.doSomething()

Produzione:

1
2
3

1
Sospetto che questa differenza di significato staticpossa essere intenzionale da parte di Apple, anche se è sempre il benvenuto a segnalare un bug per richiedere una modifica. In C, staticlimita l' archiviazione di una variabile all'ambito del file sorgente (che non è sempre lo stesso dell'ambito della classe), mentre la posizione della dichiarazione della variabile determina l' ambito lessicale (cioè globale vs all'interno di una funzione vs molti-annidati {}). In Swift, l'ambito di archiviazione segue sempre l'ambito lessicale, quindi non è possibile avere una variabile lessicale per una funzione e con archiviazione globale.
Rickster

5
Daniel, questo è in realtà leggermente (ma soprattutto) diverso da ciò che la domanda chiede. Tuttavia apprezzo la risposta. @rickster Capisco cosa stai dicendo e penso che il tuo commento potrebbe essere esteso a una buona risposta a questa domanda.
nhgrif

@nhgrif Sì, ho indicato in risposta che questo non affronta la domanda specifica. Stavo solo pensando che le modifiche in Swift 1.2 rispondessero all'esigenza principale di questo caso d'uso (sicuramente una storia migliore rispetto a Swift 1.2 precedente). Ma sembra che sia importante per te avere l'ambito della variabile per la funzione, che al momento non è possibile.
Daniel

@rickster in CI pensa che static è sempre archiviato a livello globale in realtà. Non sono sicuro però. Penso che questo sia il problema che Apple sta cercando di affrontare qui. In swift, ora è sempre lessicale e l'archiviazione è limitata alla classe
BTRUE

@nhgrif Con il mio commento precedente ho detto, penso che la risposta di Daniel in realtà dovrebbe essere la risposta accettata, perché sebbene tu possa dichiarare lessicalmente una var statica in una funzione in objc, non è stata definita lì, avendo lo stesso effetto dell'uso di un tipo statico proprietà in swift. l'unica differenza è che il punto di dichiarazione rapida è molto più descrittivo e non fuorviante su quale sia l'ambito della variabile.
BTRUE

0

Un'altra soluzione

class Myclass {
    static var timesCalled = 0
    func foo() -> Int {
        Myclass.timesCalled += 1
        return Myclass.timesCalled
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.