Variables de fonction statiques dans Swift

96

J'essaie de comprendre comment déclarer une variable statique portée uniquement localement à une fonction dans Swift.

En C, cela pourrait ressembler à ceci:

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

En Objective-C, c'est fondamentalement la même chose:

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

Mais je n'arrive pas à faire quelque chose comme ça dans Swift. J'ai essayé de déclarer la variable de la manière suivante:

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

Mais tout cela entraîne des erreurs.

  • Le premier se plaint "Les propriétés statiques ne peuvent être déclarées que sur un type".
  • Le second se plaint "Déclaration attendue" (où staticest) et "Modèle attendu" (où timesCalledBest)
  • Le troisième se plaint "Les instructions consécutives sur une ligne doivent être séparées par ';'" (dans l'espace entre les deux points et static) et "Type attendu" (où staticest)
  • Le quatrième se plaint "Les déclarations consécutives sur une ligne doivent être séparées par ';'" (dans l'espace entre Intet static) et "Déclaration attendue" (sous le signe égal)
nhgrif
la source

Réponses:

158

Je ne pense pas que Swift prend en charge la variable statique sans l'avoir attachée à une classe / structure. Essayez de déclarer une structure privée avec une variable statique.

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
Bryan Chen
la source
Ouais, j'ai continué à jouer un peu et c'était fondamentalement la solution vraiment maladroite que j'ai trouvée aussi.
nhgrif
17
Upwoted, mais je suis triste que nous devions recourir à cela.
Tricertops
1
Les propriétés et méthodes de type appartiennent à un type (c'est-à-dire une classe, une structure ou une énumération) et ne peuvent pas appartenir à une fonction seule. Documentation Apple sur les propriétés de type . @Tricertops. Une autre façon de procéder serait de mettre la fonction "foo" dans une classe, de créer une propriété de type pour cette classe et de l'utiliser dans la fonction.
NSCoder
6
@NSCoder Mais il est possible de déclarer à l' struct Holder {…}intérieur de plusieurs fonctions et elles ne se heurteront pas. Swift pourrait supporter static letsans ce passe- structpartout.
Tricertops
1
@Honey Je suis désolé mais je ne trouve pas d'autre réponse plus mise à jour?
Bryan Chen
23

Une autre solution

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

let foo = makeIncrementerClosure()
foo()  // returns 1
foo()  // returns 2
monadis
la source
3
c'est une façon javascript typique de le faire
Bryan Chen
1
Mais si j'appelle à nouveau ba (), la fonction interne renvoie 1 au premier appel. Ceci est différent d'une variable statique.
nhgrif
2
Ceci est également enseigné dans la documentation d'Apple ici: developer.apple.com/library/ios/documentation/Swift/Conceptual/ ... Cela semble être la meilleure solution juste pour rester en ligne avec la "programmation fonctionnelle", mais il existe d'autres solutions comme bien. Cela devrait cependant être la réponse acceptée.
datWooWoo
1
Je suis désolé, mais c'est un horrible hack qui ajoute plus de complexité pour le même problème. À quoi veux-tu en venir? Dans ce cas, je préfère une propriété de classe simple. La réponse de @Brian Chen est la plus proche que vous puissiez obtenir. J'utilise sa réponse pour une sorte de solution flipflop. Daniel est peut-être le meilleur conforme aux règles de programmation Swift d'Apple.
AndaluZ
1
J'ai particulièrement aimé cette solution. Ceci est un exemple parfait de l'utilisation d'une fonction d'ordre supérieur pour obtenir le même résultat qu'une variable statique dans la portée d'une fonction. Les variables de fonction statiques ne sont pas prises en charge nativement dans Swift pour de bonnes raisons. C'est l'évolution naturelle de la programmation. Essayer de coder à l'ancienne nécessite des hacks. À mon avis, l'ajout d'un type de données imbriquées supplémentaire par opposition à l'utilisation de la capture de variables réduit la lisibilité du code.
nstein
18

Swift 1.2 avec Xcode 6.3 prend désormais en charge statique comme prévu. À partir des notes de version bêta de Xcode 6.3:

Les méthodes et propriétés «statiques» sont désormais autorisées dans les classes (en tant qu'alias pour «class final»). Vous êtes maintenant autorisé à déclarer des propriétés stockées statiques dans des classes, qui ont un stockage global et sont initialisées paresseusement au premier accès (comme les variables globales). Les protocoles déclarent désormais les exigences de type comme des exigences «statiques» au lieu de les déclarer comme des exigences de «classe». (17198298)

Il semble que les fonctions ne peuvent pas contenir de déclarations statiques (comme demandé en question). Au lieu de cela, la déclaration doit être faite au niveau de la classe.

Exemple simple montrant une propriété statique incrémentée dans une fonction de classe (aka statique), bien qu'une fonction de classe ne soit pas requise:

class StaticThing
{
    static var timesCalled = 0

    class func doSomething()
    {
        timesCalled++

        println(timesCalled)
    }
}

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

Production:

1
2
3
Daniel
la source
1
Je soupçonne que cette différence dans la signification de staticpourrait être intentionnelle de la part d'Apple, bien que l'on soit toujours invité à déposer un bogue pour demander une modification. En C, staticlimite le stockage d'une variable à la portée du fichier source (qui n'est pas toujours la même que la portée de la classe), tandis que le placement de la déclaration de variable détermine la portée lexicale (c.-à-d. Global vs dans la fonction vs plusieurs-imbriqués {}). Dans Swift, la portée de stockage suit toujours la portée lexicale, vous ne pouvez donc pas avoir une variable lexicale d'une fonction et disposant d'un stockage global.
rickster
5
Daniel, c'est en fait subtilement (mais surtout) différent de ce que la question demande. J'apprécie la réponse cependant. @rickster Je comprends ce que vous dites et je pense que votre commentaire pourrait être étendu à une bonne réponse à cette question.
nhgrif
@nhgrif Oui, j'ai indiqué en réponse que cela ne répond pas à la question spécifique. Je pensais juste que les changements apportés à Swift 1.2 répondent au besoin fondamental de ce cas d'utilisation (certainement une meilleure histoire que celle d'avant Swift 1.2). Mais il semble qu'il soit important pour vous d'avoir une variable étendue à la fonction - ce qui n'est actuellement pas possible.
Daniel
@rickster dans CI pense que la statique est toujours stockée globalement en fait. Je ne suis pas sûr cependant. Je pense que c'est le problème qu'Apple essaie de résoudre ici. Dans Swift, il est maintenant toujours lexicalement et le stockage est limité à la classe
BTRUE
@nhgrif Avec mon commentaire précédent dit, je pense que la réponse de Daniel devrait en fait être la réponse acceptée, car bien que vous puissiez déclarer lexicalement une variable statique dans une fonction dans objc, elle n'a pas été portée ici, ayant le même effet que l'utilisation d'un type statique propriété à swift. la seule différence est que le point de déclaration rapide est beaucoup plus descriptif et n'est pas trompeur quant à la portée de la variable.
BTRUE
0

Une autre solution

class Myclass {
    static var timesCalled = 0
    func foo() -> Int {
        Myclass.timesCalled += 1
        return Myclass.timesCalled
    }
}
Jq
la source