bbs
oopers.com
5177 OOPer: 2014-07-19 17:20:48
Swiftいろいろ: [6] static変数
Objective-Cってのは、[]と@以外はC言語そのまんまだから、Objective-CのOS X/iOS用アプリでは、static変数が随所に使われている。
 Swiftでは、メソッド/関数内のローカル変数をstaticに宣言することはできないので、メソッドの外に追い出して、クラス変数にしちまおうかと思ったのだが…。

・Swiftのクラスプロパティは計算型プロパティだけで、ストアドプロパティは存在しない。
・現在のバージョンでは、その計算型クラスプロパティさえ実装されていない。

後者は正式版までには改善されるだろうが、前者は言語設計者のポリシーらしく、メタクラスのメソッドとしてオーバライドされたりなんかする訳だし、クラスは参照型なんだから、staticなメンバー持てるのはおかしい、ってことらしい。
 Swiftのプロパティってのは、変数宣言とgetter/setterの宣言を一つにまとめちゃったものなので、何となく納得しちゃいそうになるのだが、クラスのインスタンスが参照型でアクセスされるからと言って、メタクラスのstatic情報が存在しちゃいけないってことにはならない訳で。
 ここで文句を言っても、既にdev forumで指摘されたときに「将来はわからないが、少なくとも1.0に入る予定はない」なんて言ってるからあきらめるしかない。
(ちなみにdev forum内でのSwiftコミュニティは、Developer Tools>Languages>Swiftって、ちょっと奥まった階層にある。Objective-Cなんかと横並びだから分類的には正しいんだろうが、Appleいち押しの新言語としては、見つけにくい。言語仕様が安定するまでは、トップレベルの特設トピックとしてSwiftを扱えるようにした方が、あんまり情報の出てこないブログを開設するより有効だと思うのだが。)

で、その辺の議論もしらないまま、適当に対処したときは、
・メソッド内のstatic変数宣言をクラス変数にしようとメソッドの外に出したら怒られた。
※最初に書いたように、クラスプロパティは使えない。
・クラス宣言からも追い出してグローバル変数にしたら怒られた。
※同名のstatic変数を使っているクラスが複数あったので、変数名が衝突した。
・変数名の前に「クラス名_」なんてプレフィックスを付けた、とてもモダンな言語とは思えないグローバル変数が多数できた…。

てなことになったわけだが。dev forum内では、こんなことが出来ますよという例があげてあった。
(ちょっと書き換えてある。)
class Singleton {
    class func instance()->Singleton {
        struct My {
            static var instance: Singleton!
            static var onceToken: dispatch_once_t = 0
        }
        dispatch_once(&My.onceToken) {
            My.instance = Singleton()
        }
        return My.instance
    }
    
}
//クラスメソッドのinstance()は、read-onlyプロパティにしたいところだが、まだ使えないのでそこはあきらめて。
Singleton.instance()


既存Objective-CアプリをSwiftに書き換えながらネタ探しをしているので、シングルトンパターンをdispatch_once()で実現している部分だな。(OS X/iOSは基本マルチスレッドで動くので、気をつけないとシングルトンにならない。)
 Swiftの型宣言(class/struct/enumとか)は、ネストできるんでメソッド内でstruct宣言しちゃえば、そのstruct内ではstatic変数(ストアドのタイププロパティ)が宣言できますよって話。
(繰り返しになるが、クラス内で宣言したstructにstatic変数が持てるんだから、クラスそのものがstatic変数を持てるようにしても、実装上は何の問題もでないはず。)

もっともdispatch_once()ベースのシングルトン実装がSwift的に美しいかどうかは、また別問題なわけだが。
 Swift設計陣は、マルチスレッド環境でもstatic変数の初期化は一度しか実行されないってのを保証する気でいるらしい(この辺に限らず、現在のSwiftのドキュメントではスレッドセーフティ絡みの記述が全くないのは困り者)ので、こんなやり方も出来ちゃう訳だが。
class MyClass {
    
}
struct Singletons {
    static let MyClassInstance: MyClass = MyClass()
}

Singletons.MyClassInstance


クラスにストアドプロパティが普通に実装されさえすれば、かなりきれいに書けるんだけどね。
(dev forumには、計算型プロパティが呼び出されるたびに毎回実行されるってことを忘れてる上、文法的にも怪しい例を書き込む人もいたが…。そちらは無視。)
class SimpleSingleton {
    class let Instance = SimpleSingleton()
}
//グローバル変数やタイププロパティの初期化はデフォルトで@lazyらしいので、それは付けてない。どっちみち現在のバージョンではエラーになるんで試せないが。
SimpleSingleton.Instance


やはりクラスにもストアドプロパティが要るぞに一票。