Variables de función estática en Swift

96

Estoy tratando de averiguar cómo declarar una variable estática con alcance solo localmente a una función en Swift.

En C, esto podría verse así:

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

En Objective-C, es básicamente lo mismo:

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

Pero parece que no puedo hacer nada como esto en Swift. Intenté declarar la variable de las siguientes maneras:

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

Pero todos estos resultan en errores.

  • El primero se queja "Las propiedades estáticas solo se pueden declarar en un tipo".
  • El segundo se queja "Declaración esperada" (donde staticestá) y "Patrón esperado" (donde timesCalledBestá)
  • El tercero se queja "Las declaraciones consecutivas en una línea deben estar separadas por ';'" (en el espacio entre los dos puntos y static) y "Tipo esperado" (donde staticestá)
  • El cuarto se queja "Las declaraciones consecutivas en una línea deben estar separadas por ';'" (en el espacio entre Inty static) y "Declaración esperada" (bajo el signo igual)
nhgrif
fuente

Respuestas:

158

No creo que Swift admita una variable estática sin tenerla adjunta a una clase / estructura. Intente declarar una estructura privada con variable estática.

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
fuente
Sí, seguí jugando un poco y esta fue básicamente la solución realmente torpe que se me ocurrió.
nhgrif
17
Upwoted, pero me entristece que tengamos que recurrir a esto.
Tricertops
1
Las propiedades y métodos de tipo pertenecen a un tipo (es decir, una clase, una estructura o una enumeración) y no pueden pertenecer a una función únicamente. Documentación de Apple sobre propiedades de tipo . @Tricertops. Otra forma de hacerlo sería poner la función "foo" en una clase, crear una propiedad de tipo para esa clase y usarla dentro de la función.
NSCoder
6
@NSCoder Pero es posible declarar struct Holder {…}dentro de múltiples funciones y no colisionarán. Swift podría soportar static letsin esta structrepetición.
Tricertops
1
@Honey, lo siento pero no puedo encontrar otra respuesta más actualizada.
Bryan Chen
23

Otra solución

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
fuente
3
esta es una forma típica de JavaScript para hacerlo
Bryan Chen
1
Pero si vuelvo a llamar a ba (), la función interna devuelve 1 en la primera llamada. Esto es diferente de una variable estática.
nhgrif
2
Esto también se enseña en los documentos de Apple aquí: developer.apple.com/library/ios/documentation/Swift/Conceptual/… Parece ser la mejor solución simplemente para mantenerse en línea con la "programación funcional", pero hay otras soluciones como bien. Sin embargo, esta debería ser la respuesta aceptada.
datWooWoo
1
Lo siento, pero este es un truco feo que agrega más complejidad al mismo problema. ¿Cual es tu punto? En ese caso, prefiero una propiedad de clase simple. La respuesta de @Brian Chen es la más cercana que puedes conseguir. Utilizo su respuesta para una solución tipo flip-flop. Daniel es quizás el que mejor se ajusta a las reglas de programación Swift de Apple.
AndaluZ
1
Particularmente me gustó esta solución. Este es un ejemplo perfecto del uso de una función de orden superior para lograr el mismo resultado que una variable estática dentro del alcance de una función. Las variables de función estática no se admiten de forma nativa en Swift por buenas razones. Es la evolución natural de la programación. Tratar de codificar a la antigua requiere hacks. En mi opinión, agregar un tipo de datos anidado adicional en lugar de utilizar la captura de variables reduce la legibilidad del código.
nstein
18

Swift 1.2 con Xcode 6.3 ahora admite estática como se esperaba. De las notas de la versión beta de Xcode 6.3:

Los métodos y propiedades "estáticos" ahora están permitidos en las clases (como un alias para "clase final"). Ahora puede declarar propiedades almacenadas estáticas en clases, que tienen almacenamiento global y se inicializan lentamente en el primer acceso (como variables globales). Los protocolos ahora declaran los requisitos de tipo como requisitos "estáticos" en lugar de declararlos como requisitos de "clase". (17198298)

Parece que las funciones no pueden contener declaraciones estáticas (como se hace en la pregunta). En cambio, la declaración debe realizarse a nivel de clase.

Ejemplo simple que muestra una propiedad estática incrementada dentro de una función de clase (también conocida como estática), aunque no se requiere una función de clase:

class StaticThing
{
    static var timesCalled = 0

    class func doSomething()
    {
        timesCalled++

        println(timesCalled)
    }
}

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

Salida:

1
2
3
Daniel
fuente
1
Sospecho que esta diferencia en el significado de staticpodría ser intencional por parte de Apple, aunque siempre se puede presentar un error para solicitar un cambio. En C, staticlimita el almacenamiento de una variable al alcance del archivo fuente (que no siempre es el mismo que el alcance de la clase), mientras que la ubicación de la declaración de la variable determina el alcance léxico (es decir, global vs dentro de la función vs muchos anidados {}). En Swift, el alcance de almacenamiento siempre sigue el alcance léxico, por lo que no puede tener una variable que sea léxica para una función y que tenga almacenamiento global.
rickster
5
Daniel, esto es en realidad sutil (pero importante) diferente de lo que plantea la pregunta. Aunque agradezco la respuesta. @rickster Entiendo lo que está diciendo y creo que su comentario podría ampliarse a una buena respuesta para esta pregunta.
nhgrif
@nhgrif Sí, indiqué en respuesta que esto no aborda la pregunta específica. Estaba pensando que los cambios en Swift 1.2 abordan la necesidad central de este caso de uso (sin duda, una historia mejor que la anterior a Swift 1.2). Pero parece que es importante que tenga una variable en el ámbito de la función, lo que actualmente no es posible.
Daniel
@rickster en CI cree que la estática siempre se almacena globalmente en realidad. Aunque no estoy seguro. Creo que ese es el problema que Apple está tratando de abordar aquí. En swift, ahora siempre está léxico y el almacenamiento se limita a la clase
BTRUE
@nhgrif Con mi comentario anterior dicho, creo que la respuesta de Daniel en realidad debería ser la respuesta aceptada, porque aunque puede declarar léxicamente una var estática en una función en objc, no tenía un alcance allí, teniendo el mismo efecto que usar un Tipo estático propiedad en swift. la única diferencia es que el punto de declaración rápida es mucho más descriptivo y no engañoso en cuanto al alcance de la variable.
BTRUE
0

Otra solución

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