Статические функциональные переменные в Swift

96

Я пытаюсь понять, как объявить статическую переменную, ограниченную только локально функцией в Swift.

В C это может выглядеть примерно так:

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

В Objective-C это в основном то же самое:

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

Но я не могу делать ничего подобного в Swift. Я пробовал объявить переменную следующими способами:

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

Но все это приводит к ошибкам.

  • Первый жалуется: «Статические свойства могут быть объявлены только для типа».
  • Второй жалуется на «Ожидаемое объявление» (где staticесть) и «Ожидаемый образец» (где timesCalledBесть).
  • Третий жалуется: «Последовательные операторы в строке должны быть разделены символом ';'» (в пространстве между двоеточием и static) и «Ожидаемый тип» (где staticесть)
  • Четвертый жалуется: «Последовательные утверждения в строке должны быть разделены знаком "; "(в пробеле между Intи static) и" Ожидаемое объявление "(под знаком равенства).
nhgrif
источник

Ответы:

158

Я не думаю, что Swift поддерживает статическую переменную без привязки к классу / структуре. Попробуйте объявить частную структуру со статической переменной.

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
Брайан Чен
источник
Да, я продолжил немного поиграть, и это было действительно неуклюжее решение, которое я тоже придумал.
nhgrif
17
Upwoted, но мне жаль, что мы вынуждены прибегать к этому.
Tricertops
1
Свойства и методы типа принадлежат типу (например, классу, структуре или перечислению) и не могут принадлежать только функции. Документация Apple по свойствам типа . @Tricertops. Другой способ сделать это - поместить функцию «foo» в класс, создать свойство типа для этого класса и использовать его внутри функции.
NSCoder 02
6
@NSCoder Но можно объявлять struct Holder {…}внутри несколько функций, и они не будут конфликтовать. Swift мог бы поддерживать static letбез этого structшаблона.
Tricertops
1
@ Дорогой, извините, но я не могу найти более обновленный другой ответ?
Брайан Чен
23

Другое решение

func makeIncrementerClosure() -> () -> Int {
    var timesCalled = 0
    func incrementer() -> Int {
        timesCalled += 1
        return timesCalled
    }
    return incrementer
}

let foo = makeIncrementerClosure()
foo()  // returns 1
foo()  // returns 2
монадис
источник
3
это типичный способ сделать это с помощью javascript
Брайан Чен
1
Но если я снова вызову ba (), внутренняя функция вернет 1 при первом вызове. Это отличается от статической переменной.
nhgrif
2
Этому также учат в документации Apple здесь: developer.apple.com/library/ios/documentation/Swift/Conceptual/ ... Кажется, это лучшее решение, просто чтобы соответствовать "функциональному программированию", но есть и другие решения, такие как хорошо. Однако это должен быть принятый ответ.
datWooWoo
1
Извините, но это уродливый прием, который усложняет ту же проблему. Что ты думаешь? В этом случае я предпочитаю простое свойство класса. Ответ @Brian Chen - самый близкий из возможных. Я использую его ответ для решения вроде триггера. Дэниел, возможно, лучше всех соответствует правилам программирования на Swift от Apple.
AndaluZ 02 окт.15,
1
Мне особенно понравилось это решение. Это прекрасный пример использования функции высшего порядка для достижения того же результата, что и статическая переменная внутри области видимости функции. Статические переменные функций изначально не поддерживаются в Swift по уважительным причинам. Это естественная эволюция программирования. Попытка кодировать старомодными способами требует взлома. На мой взгляд, добавление дополнительного вложенного типа данных вместо использования захвата переменных снижает читаемость кода.
nstein
18

Swift 1.2 с Xcode 6.3 теперь поддерживает статику, как и ожидалось. Из примечаний к выпуску бета-версии Xcode 6.3:

«Статические» методы и свойства теперь разрешены в классах (как псевдоним для «class final»). Теперь вам разрешено объявлять статические сохраненные свойства в классах, которые имеют глобальное хранилище и лениво инициализируются при первом доступе (например, глобальные переменные). Протоколы теперь объявляют требования типа как «статические», а не как «классовые» требования. (17198298)

Похоже, что функции не могут содержать статические объявления (как задано в вопросе). Вместо этого объявление должно выполняться на уровне класса.

Простой пример, показывающий статическое свойство, увеличиваемое внутри функции класса (также известной как статическая), хотя функция класса не требуется:

class StaticThing
{
    static var timesCalled = 0

    class func doSomething()
    {
        timesCalled++

        println(timesCalled)
    }
}

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

Вывод:

1
2
3
Даниэль
источник
1
Я подозреваю, что эта разница в значении staticможет быть преднамеренной со стороны Apple, хотя всегда можно сообщить об ошибке, чтобы запросить изменение. В C staticограничивает хранение переменной областью видимости исходного файла (которая не всегда совпадает с областью видимости класса), в то время как размещение объявления переменной определяет лексическую область видимости (то есть глобальная или внутри функции vs многие-вложенные {}). В Swift область хранения всегда следует за лексической областью, поэтому у вас не может быть переменной, лексической для функции и имеющей глобальное хранилище.
rickster
5
Дэниел, на самом деле это тонко (но важно) отличается от того, что задается в вопросе. Но я ценю ответ. @rickster Я понимаю, о чем вы говорите, и думаю, что ваш комментарий может быть расширен до хорошего ответа на этот вопрос.
nhgrif
@nhgrif Ага, в ответ я указал, что это не касается конкретного вопроса. Я просто подумал, что изменения в Swift 1.2 удовлетворяют основную потребность в этом варианте использования (безусловно, лучшая история, чем до Swift 1.2). Но похоже, что для вас важно иметь переменную, привязанную к функции, что в настоящее время невозможно.
Дэниел
@rickster в C Я думаю, что статика всегда хранится глобально. Хотя я не уверен. Я думаю, что это проблема, которую Apple пытается решить здесь. В
Swift
@nhgrif С моим предыдущим комментарием, я думаю, что ответ Дэниела на самом деле должен быть принятым ответом, потому что, хотя вы можете лексически объявить статический var в функции в objc, он не был там ограничен, имея тот же эффект, что и использование статического типа недвижимость в Свифт. единственная разница в том, что точка быстрого объявления намного более наглядна и не вводит в заблуждение относительно того, какова область видимости переменной.
BTRUE
0

Другое решение

class Myclass {
    static var timesCalled = 0
    func foo() -> Int {
        Myclass.timesCalled += 1
        return Myclass.timesCalled
    }
}
Jq
источник