Como definir constante estática em uma classe em swift

105

Eu tenho essas definições em minha função que funcionam

class MyClass {
    func myFunc() {
        let testStr = "test"
        let testStrLen = countElements(testStr)
    }
}

Mas se eu mover 'testStr' e 'testStrLen' para o nível de classe, ele não compilará. Dizia que 'MyClass.Type não tem um membro chamado' testStr '.

class MyClass {
    let testStr = "test"
    let testStrLen = countElements(testStr)

    func myFunc() {

    }
}

Como posso consertar isso? Eu não quero pagar a penalidade por contar muito tempo de um 'teste' constante todas as vezes.

Com base no meu entendimento dos comentários abaixo, preciso fazer o seguinte:

class MyClass {
    let testStr = "test"
    let testStrLen = countElements("test")

    func myFunc() {

    }
}

Existe uma maneira de eu não precisar digitar / entrar em "teste" duas vezes? Obrigado.

n179911
fonte
3
possível duplicata de ViewControl.Type não tem um membro chamado (O valor inicial de uma propriedade não pode depender de outra propriedade.)
Martin R
1
possível duplicata de Alterar o X e Y em um CGRectMake (com uma boa solução usando uma propriedade preguiçosa)
Martin R
"Existe uma maneira de eu não precisar digitar / entrar em" teste "duas vezes?" - Sim. Mova a inicialização de testStrLen para um método init (como sugerido em uma resposta à primeira possível duplicata) ou use uma inicialização lenta (como sugerido em uma resposta à segunda possível duplicata).
Martin R

Respostas:

177

Talvez um bom idioma para declarar constantes para uma classe em Swift seja apenas usar uma estrutura chamada MyClassConstants como a seguir.

struct MyClassConstants{
    static let testStr = "test"
    static let testStrLength = countElements(testStr)

    static let arrayOfTests: [String] = ["foo", "bar", testStr]
}

Desta forma, suas constantes terão o escopo definido em uma construção declarada em vez de flutuar globalmente.

Atualizar

Eu adicionei uma constante de array estático, em resposta a um comentário perguntando sobre a inicialização de array estático. Veja os literais de array em "The Swift Programming Language".

Observe que tanto os literais de string quanto a constante de string podem ser usados ​​para inicializar o array. No entanto, como o tipo de array é conhecido, a constante inteira testStrLengthnão pode ser usada no inicializador de array.

Martin Woolstenhulme
fonte
2
Em vez disso, você não poderia simplesmente declarar as constantes como privadas estáticas? Eles não seriam mais globais?
sethfri
3
bate usando .rawValue como eu estava usando enum
DogCoffee
1
Esta não será a melhor solução se o código for para ser usado também do Objective C, uma vez que as estruturas do Swift não são traduzidas para o Objective C
mbpro
1
Qual é a diferença entre declarar staticconstantes em Structe em Classes?
Jauzee
1
@YOUNG Swift 2.2 Enumerações podem funcionar, mas geralmente enums têm uma associação semântica mais forte do que apenas constantes de agrupamento em geral. No link, os enums do Swift apresentam valores associados e valores brutos. Você poderia usar valores brutos, mas a sintaxe é MyClassConstants.testStr.rawValueacessar o valor, o que não é tão sintaticamente amigável quanto justo MyClassConstants.testStr.
Martin Woolstenhulme
72

Adicionando à resposta de @Martin ...

Se alguém planeja manter um arquivo constante de nível de aplicativo, você pode agrupar a constante com base em seu tipo ou natureza

struct Constants {
    struct MixpanelConstants {
        static let activeScreen = "Active Screen";
    }
    struct CrashlyticsConstants {
        static let userType = "User Type";
    }
}

Ligar : Constants.MixpanelConstants.activeScreen

ATUALIZAÇÃO 5/5/2019 (meio fora do tópico, mas 🤷🏽‍♂️)

Depois de ler algumas diretrizes de código e de experiências pessoais, parece que as estruturas não são a melhor abordagem para armazenar constantes globais por alguns motivos. Especialmente o código acima não impede a inicialização da estrutura. Podemos conseguir isso adicionando algum código clichê, mas há uma abordagem melhor

ENUMS

O mesmo pode ser alcançado usando um enum com uma representação mais segura e clara

enum Constants {
    enum MixpanelConstants: String {
        case activeScreen = "Active Screen";
    }
    enum CrashlyticsConstants: String {
        case userType = "User Type";
    }
}

print(Constants.MixpanelConstants.activeScreen.rawValue)
Clement Prem
fonte
2
Obrigado. Eu gosto disso, fornece uma maneira elegante de gerenciar constantes por meio de struct.
Abhijeet,
1
Com mais exemplos, adicionei uma abordagem melhor aqui
Anish Parajuli 웃
15

Se eu entendi sua pergunta corretamente, você está perguntando como você pode criar constantes de nível de classe (estáticas - no jargão C ++) de forma que você não a) replique a sobrecarga em todas as instâncias eb tenha que recalcular o que de outra forma é constante.

A linguagem evoluiu - como todo leitor sabe, mas ao testar isso no Xcode 6.3.1, a solução é:

import Swift

class MyClass {
    static let testStr = "test"
    static let testStrLen = count(testStr)

    init() {
        println("There are \(MyClass.testStrLen) characters in \(MyClass.testStr)")
    }
}

let a = MyClass()

// -> There are 4 characters in test

Não sei se a estática é estritamente necessária, pois o compilador certamente adiciona apenas uma entrada por variável const na seção estática do binário, mas isso afeta a sintaxe e o acesso. Usando estático, você pode se referir a ele mesmo quando você não tem uma instância: MyClass.testStrLen.

Chris Conover
fonte
11

Se você realmente deseja uma propriedade estática de sua classe, isso não é atualmente suportado no Swift. O conselho atual é contornar isso usando constantes globais:

let testStr = "test"
let testStrLen = countElements(testStr)

class MyClass {
    func myFunc() {
    }
}

Se você quiser que essas propriedades sejam de instância, em vez disso, você pode usar uma propriedade lazy stored para o comprimento - ela só será avaliada na primeira vez que for acessada, portanto, você não a computará continuamente.

class MyClass {
    let testStr: String = "test"
    lazy var testStrLen: Int = countElements(self.testStr)

    func myFunc() {
    }
}
Nate Cook
fonte
1
1 Pena que ainda não parece melhor maneira do que essas variáveis ​​globais.
Drux
1
Eu sei que esta é uma restrição de linguagem no momento, mas prefiro evitar usar globais a todo custo. Acho que usar contêineres de estrutura com algo como ClassNameConstants seria uma abordagem melhor por enquanto.
Zorayr
7

Que tal usar propriedades computadas?

class MyClass {
  class var myConstant: String { return "What is Love? Baby don't hurt me" }
}

MyClass.myConstant
Emin Bugra Saral
fonte
7

Alguns podem querer certas constantes de classe públicas, enquanto outras, privadas.

A palavra-chave privada pode ser usada para limitar o escopo das constantes no mesmo arquivo swift.

class MyClass {

struct Constants {

    static let testStr = "test"
    static let testStrLen = testStr.characters.count

    //testInt will not be accessable by other classes in different swift files
    private static let testInt = 1
}

func ownFunction()
{

    var newInt = Constants.testInt + 1

    print("Print testStr=\(Constants.testStr)")
}

}

Outras classes serão capazes de acessar suas constantes de classe como abaixo

class MyClass2
{

func accessOtherConstants()
{
    print("MyClass's testStr=\(MyClass.Constants.testStr)")
}

} 
ChinLoong
fonte
2

Julgado no Playground


class MyClass {

struct Constants { static let testStr = "test" static let testStrLen = testStr.characters.count //testInt will not be accessable by other classes in different swift files private static let testInt = 1 static func singletonFunction() { //accessable print("Print singletonFunction testInt=\(testInt)") var newInt = testStrLen newInt = newInt + 1 print("Print singletonFunction testStr=\(testStr)") } } func ownFunction() { //not accessable //var newInt1 = Constants.testInt + 1 var newInt2 = Constants.testStrLen newInt2 = newInt2 + 1 print("Print ownFunction testStr=\(Constants.testStr)") print("Print ownFunction newInt2=\(newInt2)") } } let newInt = MyClass.Constants.testStrLen print("Print testStr=\(MyClass.Constants.testStr)") print("Print testInt=\(newInt)") let myClass = MyClass() myClass.ownFunction() MyClass.Constants.singletonFunction()
Kevin6
fonte
Como isso está relacionado à pergunta?
Franklin Yu