Comment définir une constante statique dans une classe dans Swift

105

J'ai ces définitions dans ma fonction qui fonctionnent

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

Mais si je déplace «testStr» et «testStrLen» au niveau de la classe, il ne se compilera pas. Il a dit 'MyClass.Type n'a pas de membre nommé' testStr '.

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

    func myFunc() {

    }
}

Comment puis-je réparer cela? Je ne veux pas payer la pénalité pour compter len d'un «test» constant à chaque fois.

Sur la base de ma compréhension des commentaires ci-dessous, je dois faire ceci:

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

    func myFunc() {

    }
}

Y a-t-il un moyen que je n'ai pas besoin de taper / entrer «test» deux fois? Merci.

n179911
la source
3
duplication possible de ViewControl.Type n'a pas de membre nommé (La valeur initiale d'une propriété ne peut pas dépendre d'une autre propriété.)
Martin R
1
duplication possible de Change the X et Y dans un CGRectMake (avec une belle solution utilisant une propriété paresseuse)
Martin R
"Est-il possible que je n'ai pas besoin de taper / saisir" test "deux fois?" - Oui. Déplacez l'initialisation de testStrLen dans une méthode init (comme suggéré dans une réponse au premier doublon possible), ou utilisez une initialisation paresseuse (comme suggéré dans une réponse au deuxième duplicata possible).
Martin R

Réponses:

177

Peut-être qu'un idiome intéressant pour déclarer des constantes pour une classe dans Swift est d'utiliser simplement une structure nommée MyClassConstants comme suit.

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

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

De cette façon, vos constantes seront étendues dans une construction déclarée au lieu de flotter globalement.

Mettre à jour

J'ai ajouté une constante de tableau statique, en réponse à un commentaire demandant l'initialisation du tableau statique. Voir Array Literals dans "The Swift Programming Language".

Notez que les littéraux de chaîne et la constante de chaîne peuvent être utilisés pour initialiser le tableau. Cependant, comme le type de tableau est connu, la constante entière testStrLengthne peut pas être utilisée dans l'initialiseur de tableau.

Martin Woolstenhulme
la source
2
Ne pourriez-vous pas simplement déclarer les constantes comme static private? Ne seraient-ils plus alors mondiaux?
sethfri
3
bat en utilisant .rawValue comme j'utilisais enum
DogCoffee
1
Ce ne sera pas la meilleure solution si le code doit également être utilisé depuis Objective C, car les structures Swift ne sont pas traduites en Objective C
mbpro
1
Quelle est la différence entre déclarer des staticconstantes dans Structet dans Classes?
Jauzee
1
@YOUNG Swift 2.2 Enumérations pourraient fonctionner, mais généralement les énumérations ont une association sémantique plus forte que le simple regroupement de constantes en général. Dans le lien, les énumérations Swift présentent à la fois des valeurs associées et des valeurs brutes. Vous pouvez utiliser des valeurs brutes, mais la syntaxe consiste MyClassConstants.testStr.rawValueà accéder à la valeur, ce qui n'est pas aussi convivial du point de vue syntaxique que juste MyClassConstants.testStr.
Martin Woolstenhulme
72

Ajout à la réponse de @ Martin ...

Si quelqu'un envisage de conserver un fichier de constante au niveau de l'application, vous pouvez regrouper la constante en fonction de son type ou de sa nature

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

Appel : Constants.MixpanelConstants.activeScreen

MISE À JOUR 5/5/2019 (un peu hors sujet mais 🤷🏽‍♂️)

Après avoir lu quelques directives de code et des expériences personnelles, il semble que les structures ne soient pas la meilleure approche pour stocker des constantes globales pour plusieurs raisons. En particulier, le code ci-dessus n'empêche pas l'initialisation de la structure. Nous pouvons y parvenir en ajoutant un code standard, mais il existe une meilleure approche

ENUMS

La même chose peut être obtenue en utilisant une énumération avec une représentation plus sûre et plus claire

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

print(Constants.MixpanelConstants.activeScreen.rawValue)
Clément Prem
la source
2
Je vous remercie. J'aime cela, fournit une manière élégante de gérer les constantes via struct.
Abhijeet
1
Avec plus d'exemples, j'ai ajouté une meilleure approche ici
Anish Parajuli 웃
15

Si je comprends bien votre question, vous demandez comment vous pouvez créer des constantes au niveau de la classe (statiques - dans le langage C ++) de manière à ne pas a) répliquer la surcharge dans chaque instance, et b devoir recalculer ce qui est par ailleurs constant.

Le langage a évolué - comme chaque lecteur le sait, mais comme je le teste dans Xcode 6.3.1, la solution est:

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

Je ne sais pas si le statique est strictement nécessaire car le compilateur n'ajoute sûrement qu'une seule entrée par variable const dans la section statique du binaire, mais cela affecte la syntaxe et l'accès. En utilisant statique, vous pouvez vous référer à même lorsque vous ne disposez pas d' une instance: MyClass.testStrLen.

Chris Conover
la source
11

Si vous voulez réellement une propriété statique de votre classe, cela n'est actuellement pas pris en charge dans Swift. Le conseil actuel est de contourner cela en utilisant des constantes globales:

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

class MyClass {
    func myFunc() {
    }
}

Si vous souhaitez plutôt qu'il s'agisse de propriétés d'instance, vous pouvez utiliser une propriété stockée différée pour la longueur - elle ne sera évaluée que lors du premier accès, vous ne la calculerez donc pas encore et encore.

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

    func myFunc() {
    }
}
Nate Cook
la source
1
+1 Dommage qu'il ne semble toujours pas meilleur moyen que ces variables globales.
Drux
1
Je sais que c'est une contrainte de langage pour le moment, mais je préférerais éviter à tout prix d'utiliser des globaux. Je pense que l'utilisation de conteneurs struct avec quelque chose comme ClassNameConstants serait une meilleure approche pour le moment.
Zorayr
7

Qu'en est-il des propriétés calculées?

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

MyClass.myConstant
Emin Bugra Saral
la source
7

Certains voudront peut-être que certaines constantes de classe soient publiques, d'autres privées.

Le mot-clé private peut être utilisé pour limiter la portée des constantes dans le même fichier 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)")
}

}

D'autres classes pourront accéder à vos constantes de classe comme ci-dessous

class MyClass2
{

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

} 
ChinLoong
la source
2

Essayé sur 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
la source
Comment cela est-il lié à la question?
Franklin Yu