Статические функциональные переменные в 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) и" Ожидаемое объявление "(под знаком равенства).

Ответы:


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
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.