Statyczne zmienne funkcyjne w Swift

97

Próbuję dowiedzieć się, jak zadeklarować zmienną statyczną w zakresie tylko lokalnie do funkcji w języku Swift.

W C może to wyglądać mniej więcej tak:

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

W Objective-C jest w zasadzie to samo:

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

Ale wydaje mi się, że nie mogę zrobić czegoś takiego w Swift. Próbowałem zadeklarować zmienną w następujący sposób:

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

Ale to wszystko prowadzi do błędów.

  • Pierwsza narzeka: „Właściwości statyczne mogą być deklarowane tylko dla typu”.
  • Drugi narzeka „Oczekiwana deklaracja” (gdzie staticjest) i „Oczekiwany wzorzec” (gdzie timesCalledBjest)
  • Trzeci narzeka „Kolejne instrukcje w wierszu muszą być oddzielone znakiem„; ”” (w odstępie między dwukropkiem a static) i „Oczekiwany typ” (gdzie staticjest)
  • Czwarty dotyczy „Kolejne stwierdzenia w wierszu muszą być oddzielone znakiem„; ”” (w spacji między Inti static) oraz „Oczekiwana deklaracja” (pod znakiem równości)
nhgrif
źródło

Odpowiedzi:

159

Myślę, że Swift nie obsługuje zmiennej statycznej bez dołączenia jej do klasy / struktury. Spróbuj zadeklarować strukturę prywatną ze zmienną statyczną.

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
Bryan Chen
źródło
Tak, kontynuowałem trochę zabawy i było to w zasadzie naprawdę niezgrabne rozwiązanie, które również wymyśliłem.
nhgrif
17
Upwoted, ale przykro mi, że musimy się do tego uciec.
Tricertops
1
Właściwości i metody typu należą do typu (tj. Class, Struct lub Enum) i nie mogą należeć do samej funkcji. Dokumentacja Apple dotycząca właściwości typu . @Tricertops. Innym sposobem byłoby umieszczenie funkcji „foo” w klasie, utworzenie właściwości type dla tej klasy i użycie jej wewnątrz funkcji.
NSCoder
6
@NSCoder Ale można zadeklarować struct Holder {…}wewnątrz wielu funkcji i nie będą się one kolidować. Swift mógłby wytrzymać static letbez tego structschematu.
Tricertops
1
@ Kochanie, przepraszam, ale nie mogę znaleźć bardziej aktualnej innej odpowiedzi?
Bryan Chen
23

Inne rozwiązanie

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

let foo = makeIncrementerClosure()
foo()  // returns 1
foo()  // returns 2
monadis
źródło
3
jest to typowy sposób na javascript
Bryan Chen
1
Ale jeśli ponownie wywołam ba (), funkcja wewnętrzna zwraca 1 przy pierwszym wywołaniu. Różni się to od zmiennej statycznej.
nhgrif
2
Jest to również nauczane w dokumentach Apple tutaj: developer.apple.com/library/ios/documentation/Swift/Conceptual/… Wydaje się, że najlepszym rozwiązaniem jest po prostu trzymanie się linii „programowania funkcjonalnego”, ale są też inne rozwiązania, takie jak dobrze. To powinna być jednak akceptowana odpowiedź.
datWooWoo
1
Przepraszam, ale to jest brzydki hack, który zwiększa złożoność tego samego problemu. O co ci chodzi? W takim przypadku wolę prostą właściwość klasy. Odpowiedź @Brian Chen jest najbliższa, jaką możesz uzyskać. Używam jego odpowiedzi jako rozwiązania typu flipflop. Daniel jest prawdopodobnie najlepiej dostosowanym do zasad Apple dotyczących programowania w języku Swift.
AndaluZ
1
Szczególnie podobało mi się to rozwiązanie. Jest to doskonały przykład użycia funkcji wyższego rzędu w celu uzyskania tego samego wyniku, co zmienna statyczna w zakresie funkcji. Zmienne funkcji statycznych nie są natywnie obsługiwane w Swift z ważnych powodów. To naturalna ewolucja programowania. Próba kodowania w staromodny sposób wymaga hacków. Moim zdaniem dodanie dodatkowego zagnieżdżonego typu danych w przeciwieństwie do przechwytywania zmiennych zmniejsza czytelność kodu.
nstein
18

Swift 1.2 z Xcode 6.3 obsługuje teraz statyczne zgodnie z oczekiwaniami. Z informacji o wersji beta Xcode 6.3:

„Statyczne” metody i właściwości są teraz dozwolone w klasach (jako alias dla „klasy ostatecznej”). Możesz teraz zadeklarować statyczne właściwości przechowywane w klasach, które mają globalną pamięć masową i są leniwie inicjowane przy pierwszym dostępie (jak zmienne globalne). Protokoły teraz deklarują wymagania typu jako wymagania „statyczne” zamiast deklarować je jako wymagania „klasy”. (17198298)

Wygląda na to, że funkcje nie mogą zawierać deklaracji statycznych (jak zadano w pytaniu). Zamiast tego deklarację należy wykonać na poziomie klasy.

Prosty przykład pokazujący statyczną właściwość inkrementowaną wewnątrz funkcji klasowej (aka statycznej), chociaż funkcja klasy nie jest wymagana:

class StaticThing
{
    static var timesCalled = 0

    class func doSomething()
    {
        timesCalled++

        println(timesCalled)
    }
}

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

Wynik:

1
2
3
Daniel
źródło
1
Podejrzewam, że ta różnica w znaczeniu staticmoże być celowa ze strony Apple, chociaż zawsze można zgłosić błąd, aby poprosić o zmianę. W C staticogranicza przechowywanie zmiennej do zakresu pliku źródłowego (który nie zawsze jest taki sam jak zakres klasy), podczas gdy umieszczenie deklaracji zmiennej określa zakres leksykalny (tj. Globalny vs wewnątrz funkcji vs wiele zagnieżdżonych {}). W języku Swift zakres pamięci zawsze jest zgodny z zakresem leksykalnym, więc nie możesz mieć zmiennej, która jest leksykalna dla funkcji i ma pamięć globalną.
rickster
5
Daniel, w rzeczywistości jest to subtelnie (ale co ważne) inne niż to, o co chodzi w pytaniu. Doceniam jednak odpowiedź. @rickster Rozumiem, co mówisz, i myślę, że Twój komentarz można rozszerzyć, tak aby zawierał dobrą odpowiedź na to pytanie.
nhgrif
@nhgrif Tak, w odpowiedzi wskazałem, że nie dotyczy to konkretnego pytania. Właśnie pomyślałem, że zmiany w Swift 1.2 odpowiadają podstawowym potrzebom w tym przypadku użycia (z pewnością lepsza historia niż w wersji wcześniejszej niż Swift 1.2). Ale wygląda na to, że ważne jest, aby mieć zakres zmiennej obejmujący funkcję - co obecnie nie jest możliwe.
Daniel
@rickster w CI uważa, że ​​statyczne dane są zawsze przechowywane globalnie. Nie jestem jednak pewien. Myślę, że to jest problem, który Apple próbuje tutaj rozwiązać. W skrócie, jest teraz zawsze leksykalnie i przechowywanie ograniczone do klasy
BTRUE Kwietnia
@nhgrif Zgodnie z moim poprzednim komentarzem, myślę, że odpowiedź Daniela w rzeczywistości powinna być akceptowaną odpowiedzią, ponieważ chociaż możesz leksykalnie zadeklarować statyczną zmienną w funkcji w objc, nie ma tam zakresu, co daje taki sam efekt jak użycie statycznego Type nieruchomość w szybkim tempie. jedyną różnicą jest to, że punkt szybkiej deklaracji jest o wiele bardziej opisowy i nie wprowadza w błąd co do zakresu zmiennej.
BTRUE
0

Inne rozwiązanie

class Myclass {
    static var timesCalled = 0
    func foo() -> Int {
        Myclass.timesCalled += 1
        return Myclass.timesCalled
    }
}
Jq
źródło