Variáveis ​​de função estática em Swift

96

Estou tentando descobrir como declarar uma variável estática com escopo apenas localmente para uma função em Swift.

Em C, isso pode ser parecido com isto:

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

Em Objective-C, é basicamente o mesmo:

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

Mas não consigo fazer nada parecido com Swift. Tentei declarar a variável das seguintes maneiras:

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

Mas tudo isso resulta em erros.

  • O primeiro reclama "As propriedades estáticas só podem ser declaradas em um tipo".
  • O segundo reclama "Declaração esperada" (onde staticestá) e "Padrão esperado" (onde timesCalledBestá)
  • O terceiro reclama "As declarações consecutivas em uma linha devem ser separadas por ';'" (no espaço entre os dois pontos e static) e "Tipo esperado" (onde staticestá)
  • O quarto reclama "Declarações consecutivas em uma linha devem ser separadas por ';'" (no espaço entre Inte static) e "Declaração esperada" (sob o sinal de igual)
nhgrif
fonte

Respostas:

158

Eu não acho que o Swift oferece suporte a variável estática sem ter anexado a uma classe / estrutura. Tente declarar uma estrutura privada com variável 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
fonte
Sim, eu continuei brincando um pouco e essa foi basicamente a solução realmente desajeitada que eu inventei também.
nhgrif
17
Motivado, mas estou triste por termos que recorrer a isso.
Tricertops
1
As propriedades e métodos de tipo pertencem a um tipo (ou seja, uma classe, estrutura ou enum) e não podem pertencer apenas a uma função. Documentação da Apple sobre propriedades de tipo . @Tricertops. Outra maneira de fazer isso seria colocar a função "foo" em uma classe, criar uma propriedade de tipo para essa classe e usá-la dentro da função.
NSCoder
6
@NSCoder Mas é possível declarar struct Holder {…}dentro de várias funções e elas não entrarão em conflito. Swift poderia suportar static letsem este structclichê ao redor.
Tricertops de
1
@Honey, desculpe, mas não consigo encontrar outra resposta mais atualizada?
Bryan Chen
23

Outra solução

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
fonte
3
esta é uma maneira típica de javascript de fazer isso
Bryan Chen
1
Mas se eu chamar ba () novamente, a função interna retorna 1 na primeira chamada. Isso é diferente de uma variável estática.
nhgrif
2
Isso também é ensinado nos documentos da Apple aqui: developer.apple.com/library/ios/documentation/Swift/Conceptual/… Parece ser a melhor solução apenas para se manter em linha com a "programação funcional", mas existem outras soluções como bem. Esta deve ser a resposta aceita.
datWooWoo
1
Sinto muito, mas este é um hack feio que adiciona mais complexidade para o mesmo problema. Onde você quer chegar? Nesse caso, prefiro uma propriedade de classe simples. A resposta de @Brian Chen é a mais próxima que você pode obter. Eu uso sua resposta como uma espécie de solução flipflop. Daniel é talvez o que melhor conforma as regras de programação Swift da Apple.
AndaluZ,
1
Gostei particularmente desta solução. Este é um exemplo perfeito de como usar uma função de ordem superior para obter o mesmo resultado que uma variável estática dentro do escopo de uma função. Vars de função estática não são nativamente suportadas no Swift por boas razões. É a evolução natural da programação. Tentar codificar à moda antiga requer hacks. Em minha opinião, adicionar um tipo de dados aninhado extra em vez de utilizar a captura de variável reduz a legibilidade do código.
nstein
18

Swift 1.2 com Xcode 6.3 agora oferece suporte estático conforme o esperado. Das notas de lançamento do Xcode 6.3 beta:

Métodos e propriedades “estáticos” agora são permitidos nas classes (como um apelido para “final da classe”). Agora você tem permissão para declarar propriedades armazenadas estáticas em classes, que têm armazenamento global e são inicializadas lentamente no primeiro acesso (como variáveis ​​globais). Os protocolos agora declaram requisitos de tipo como requisitos “estáticos” em vez de declará-los como requisitos de “classe”. (17198298)

Parece que as funções não podem conter declarações estáticas (como perguntado na pergunta). Em vez disso, a declaração deve ser feita no nível da classe.

Exemplo simples mostrando uma propriedade estática incrementada dentro de uma função de classe (também conhecida como estática), embora uma função de classe não seja necessária:

class StaticThing
{
    static var timesCalled = 0

    class func doSomething()
    {
        timesCalled++

        println(timesCalled)
    }
}

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

Resultado:

1
2
3
Daniel
fonte
1
Suspeito que essa diferença no significado de staticpode ser intencional da parte da Apple, embora seja sempre bem-vindo para registrar um bug para solicitar uma alteração. Em C, staticlimita o armazenamento de uma variável ao escopo do arquivo-fonte (que nem sempre é o mesmo que o escopo da classe), enquanto o posicionamento da declaração da variável determina o escopo léxico (isto é, global vs dentro da função vs muitos-aninhados- {}s). Em Swift, o escopo de armazenamento sempre segue o escopo léxico, portanto, você não pode ter uma variável que seja lexical para uma função e que tenha armazenamento global.
rickster
5
Daniel, isso é sutilmente (mas importante) diferente do que a pergunta pede. Agradeço a resposta. @rickster Eu entendo o que você está dizendo e acho que seu comentário poderia ser expandido para uma boa resposta para esta pergunta.
nhgrif
@nhgrif Sim, indiquei na resposta que isso não abordava a questão específica. Eu estava pensando que as mudanças no Swift 1.2 atendem à necessidade central desse caso de uso (certamente uma história melhor do que antes do Swift 1.2). Mas parece que é importante para você ter um escopo de variável para a função - o que atualmente não é possível.
Daniel
@rickster em CI acha que a estática sempre é armazenada globalmente, na verdade. Eu não tenho certeza embora. Acho que é esse o problema que a Apple está tentando resolver aqui. Em swift, agora é sempre lexicalmente e o escopo de armazenamento é a classe
BTRUE
@nhgrif Com meu comentário anterior dito, acho que a resposta de Daniel realmente deveria ser aceita, porque embora você possa declarar lexicamente uma var estática em uma função em objc, ela não tinha o escopo lá, tendo o mesmo efeito de usar um tipo estático propriedade em rápido. a única diferença é que o ponto de declaração rápida é muito mais descritivo e não enganoso quanto ao escopo da variável.
BTRUE de
0

Outra solução

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