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

しかし、これらはすべてエラーになります。

  • 最初のメッセージは、「静的プロパティは型でのみ宣言できる」というものです。
  • 2番目は、「予期された宣言」(どこにstaticあるか)および「予期されたパターン」(どこにtimesCalledBあるか)を不平を言います
  • 3番目は、「行の連続するステートメントは、コロンとの間のスペースで「;」で区切る必要があります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 2014

17
腹が立つが、私はこれに頼らなければならないのが悲しい。
Tricertops

1
型のプロパティとメソッドは、型(つまり、クラス、構造体、または列挙型)に属し、関数だけに属することはできません。タイププロパティに関するAppleドキュメント。@Tricertops。これを実行する別の方法は、関数に "foo"をクラスに入れ、そのクラスのタイププロパティを作成し、関数内で使用することです。
NSCoder 2016年

6
@NSCoderしかし、struct Holder {…}複数の関数の内部で宣言することは可能であり、それらは衝突しません。Swiftはstatic letこのstruct定型文がなくてもサポートできます。
Tricertops 2016年

1
@ハニー申し訳ありませんが、更新された他の回答が見つかりませんか?
Bryan Chen

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 2014年

2
これは、Appleのドキュメント(developer.apple.com/library/ios/documentation/Swift/Conceptual/…)でも教えられています。 「関数型プログラミング」に合わせるだけで最適なソリューションのようですが、他にも次のようなソリューションがあります。上手。これは受け入れられる答えですが。
datWooWoo 2015年

1
申し訳ありませんが、これは醜いハックであり、同じ問題にさらに複雑さを加えています。あなたのポイントは何ですか?その場合は、単純なクラスプロパティを使用します。@Brian Chenの答えは、最も近いものです。私はフリップフロップのようなソリューションに彼の答えを使います。ダニエルは多分AppleのSwiftプログラミングの規則に最も適合するでしょう。
AndaluZ、2015年

1
私はこのソリューションが特に気に入りました。これは、高次関数を使用して、関数のスコープ内の静的変数と同じ結果を得る完璧な例です。静的関数の変数は、Swiftでは本来サポートされていません。それはプログラミングの自然な進化です。古い方法でコーディングしようとすると、ハックが必要になります。私の意見では、変数のキャプチャーを使用するのではなく、ネストされたデータ型を追加すると、コードが読みにくくなります。
nstein

18

Xcode 6.3を搭載したSwift 1.2は、期待どおり静的をサポートするようになりました。Xcode 6.3ベータリリースノートから:

「静的」なメソッドとプロパティがクラスで許可されるようになりました(「クラスファイナル」のエイリアスとして)。グローバルストレージを持ち、最初のアクセス時に遅延して初期化される(グローバル変数のような)クラスで静的に格納されたプロパティを宣言できるようになりました。プロトコルは、タイプ要件を「クラス」要件として宣言するのではなく、「静的」要件として宣言するようになりました。(17198298)

関数には静的な宣言を含めることはできないようです(質問のとおり)。代わりに、宣言はクラスレベルで行う必要があります。

クラス(別名static)関数内でインクリメントされる静的プロパティを示す簡単な例ですが、クラス関数は必要ありません。

class StaticThing
{
    static var timesCalled = 0

    class func doSomething()
    {
        timesCalled++

        println(timesCalled)
    }
}

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

出力:

1
2
3

1
この意味の違いはstaticApple側の意図的なものかもしれないと思いますが、変更をリクエストするためにバグを報告することはいつでも歓迎です。Cでは、変数の格納をソースファイルスコープ(常にクラススコープと同じであるとは限りません)にstatic制限し、変数宣言の配置が字句スコープを決定します(つまり、グローバルvs関数内vs多ネスト)。Swiftでは、ストレージスコープは常にレキシカルスコープに従います。そのため、関数に対してレキシカルで、グローバルストレージを持つ変数を持つことはできません。{}
リクスター

5
ダニエル、これは実際には微妙な(しかし重要な)質問の質問とは異なります。私は答えを感謝します。@rickster私はあなたの言っていることを理解しており、あなたのコメントはこの質問に対する良い答えに拡張できると思います。
nhgrif 2015

@nhgrifうん、私はこれが特定の質問に対処しないことを答えで示した。私は、Swift 1.2の変更がこのユースケースのコアニーズに対応していると考えていました(確かに、Swift 1.2以前よりも良い話です)。しかし、変数のスコープを関数に設定することが重要であるように思えますが、これは現在不可能です。
ダニエル

CIの@ricksterは、静的は常にグローバルに実際に格納されると考えています。よくわかりません。これがAppleがここで取り組もうとしている問題だと思う。迅速に、それは常に
レキシカル

@nhgrif前のコメントで述べたように、ダニエルの答えは実際には受け入れられる答えであるはずです。objcの関数で静的変数をレキシカルに宣言することはできますが、そこにはスコープがなく、静的タイプを使用するのと同じ効果があります。迅速なプロパティ。唯一の違いは、迅速な宣言ポイントがはるかに記述的であり、変数のスコープが何であるかについて誤解を招かないことです。
BTRUE

0

別の解決策

class Myclass {
    static var timesCalled = 0
    func foo() -> Int {
        Myclass.timesCalled += 1
        return Myclass.timesCalled
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.