Как определить статическую константу в классе в Swift

105

У меня есть эти определения в моей функции, которые работают

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

Но если я переведу testStr и testStrLen на уровень класса, он не будет компилироваться. Он сказал, что MyClass.Type не имеет члена с именем testStr.

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

    func myFunc() {

    }
}

Как я могу это исправить? Я не хочу платить штраф за то, что каждый раз подсчитывает len постоянной «проверки».

Исходя из моих представлений о комментариях ниже, мне нужно сделать следующее:

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

    func myFunc() {

    }
}

Есть ли способ, которым мне не нужно вводить «тест» дважды? Спасибо.

n179911
источник
3
возможный дубликат ViewControl.Type не имеет указанного члена (начальное значение свойства не может зависеть от другого свойства.)
Мартин Р.
1
возможный дубликат изменения X и Y в CGRectMake (с хорошим решением, использующим ленивое свойство)
Martin R
"Есть ли способ, которым мне не нужно дважды вводить" тест "?" - Да. Переместите инициализацию testStrLen в метод инициализации (как предлагается в ответе на первый возможный дубликат) или используйте ленивую инициализацию (как предложено в ответе на второй возможный дубликат).
Martin R

Ответы:

177

Возможно, хорошая идиома для объявления констант для класса в Swift - просто использовать структуру с именем MyClassConstants, как показано ниже.

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

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

Таким образом, ваши константы будут ограничены объявленной конструкцией, а не перемещаться по всему миру.

Обновить

Я добавил константу статического массива в ответ на комментарий, спрашивающий об инициализации статического массива. См. Литералы массивов в разделе «Язык программирования Swift».

Обратите внимание, что для инициализации массива можно использовать как строковые литералы, так и строковую константу. Однако, поскольку тип массива известен, целочисленную константу testStrLengthнельзя использовать в инициализаторе массива.

Мартин Вулстенхалм
источник
2
Не могли бы вы вместо этого просто объявить константы статическими частными? Разве они не перестали бы быть глобальными?
sethfri
3
превосходит использование .rawValue, как я использовал enum
DogCoffee
1
Это не будет лучшим решением, если код будет использоваться и из Objective C, поскольку структуры Swift не транслируются в Objective C
mbpro
1
В чем разница между объявлением staticконстант в Structи в Classes?
Jauzee
1
@YOUNG Swift 2.2 Перечисления могут работать, но обычно перечисления имеют более сильную семантическую ассоциацию, чем просто группирующие константы в целом. В ссылке перечисления Swift содержат как связанные значения, так и необработанные значения. Вы можете использовать необработанные значения, но синтаксис предназначен MyClassConstants.testStr.rawValueдля доступа к значению, что не так синтаксически дружественно, как просто MyClassConstants.testStr.
Мартин Вулстенхалм,
72

Добавление к ответу @Martin ...

Если кто-то планирует сохранить файл констант на уровне приложения, вы можете сгруппировать константы по их типу или характеру.

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

Вызов : Constants.MixpanelConstants.activeScreen

ОБНОВЛЕНИЕ 5/5/2019 (вроде не по теме, но)

После прочтения некоторых рекомендаций по коду и из личного опыта кажется, что структуры - не лучший подход для хранения глобальных констант по нескольким причинам. В частности, приведенный выше код не препятствует инициализации структуры. Мы можем добиться этого, добавив шаблонный код, но есть лучший подход.

ENUMS

То же самое может быть достигнуто с помощью перечисления с более безопасным и понятным представлением.

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

print(Constants.MixpanelConstants.activeScreen.rawValue)
Клемент Прем
источник
2
Спасибо. Мне это нравится, предоставляет элегантный способ управления константами через структуру.
Abhijeet
1
С дополнительными примерами я добавил сюда
Аниш Параджули 웃
15

Если я правильно понимаю ваш вопрос, вы спрашиваете, как вы можете создавать константы уровня класса (статические - на языке C ++), чтобы вы не: а) реплицировали накладные расходы в каждом случае, и b приходилось пересчитывать то, что в противном случае является постоянным.

Язык эволюционировал - как известно каждому читателю, но пока я тестирую это в Xcode 6.3.1, решение следующее:

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

Я не знаю, является ли статика строго необходимой, поскольку компилятор наверняка добавляет только одну запись для каждой константной переменной в статический раздел двоичного файла, но это влияет на синтаксис и доступ. Используя статические, вы можете обратиться к нему , даже если у вас нет экземпляра: MyClass.testStrLen.

Крис Коновер
источник
11

Если вам действительно нужно статическое свойство вашего класса, оно в настоящее время не поддерживается в Swift. Текущий совет состоит в том, чтобы обойти это, используя глобальные константы:

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

class MyClass {
    func myFunc() {
    }
}

Если вы хотите, чтобы это были свойства экземпляра, вы можете использовать ленивое сохраненное свойство для длины - оно будет оцениваться только при первом обращении, поэтому вы не будете вычислять его снова и снова.

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

    func myFunc() {
    }
}
Нейт Кук
источник
1
+1 Жаль, что все же, кажется, нет лучшего способа, чем эти глобальные переменные.
Drux
1
Я знаю, что на данный момент это языковое ограничение, но я бы предпочел любой ценой избегать использования глобальных переменных. Я думаю, что использование структурных контейнеров с чем-то вроде ClassNameConstants было бы лучшим подходом на данный момент.
Зорайр
7

А как насчет использования вычисленных свойств?

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

MyClass.myConstant
Эмин Бугра Сарал
источник
7

Некоторые могут захотеть, чтобы определенные константы класса были общедоступными, а другие - частными.

Ключевое слово private можно использовать для ограничения объема констант в одном и том же файле 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)")
}

}

Другие классы смогут получить доступ к константам вашего класса, как показано ниже

class MyClass2
{

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

} 
ChinLoong
источник
2

Пробовал на игровой площадке


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()
Кевин6
источник
Как это связано с вопросом?
Франклин Ю