さて、Xcode 8の新しい Swifty Dispatch API について知りました。DispatchQueue.main.async
を使用して楽しんでおり、Dispatch
モジュールをブラウジングしていますすべての新しいAPIを見つけるためのXcode。
しかし、私はdispatch_once
を使用して、シングルトン作成や1回限りのセットアップなどが(マルチスレッド環境でも)複数回実行されないようにします...そしてdispatch_once
はどこにもありません新しいDispatchモジュールで見つかりましたか?
static var token: dispatch_once_t = 0
func whatDoYouHear() {
print("All of this has happened before, and all of it will happen again.")
dispatch_once(&token) {
print("Except this part.")
}
}
Swift 1.x以降、Swiftはdispatch_once
舞台裏 を使用して、グローバル変数と静的プロパティのスレッドセーフな遅延初期化を実行しました。 。
そのため、上記のstatic var
は既にdispatch_once
を使用していたため、奇妙なものになります(そして、別のdispatch_once
のトークンとして再び使用すると問題が発生する可能性があります。この種の再帰なしでdispatch_once
を使用するので、彼らはそれを取り除きました。代わりに、それに構築された言語機能を使用します。
// global constant: SomeClass initializer gets called lazily, only on first use
let foo = SomeClass()
// global var, same thing happens here
// even though the "initializer" is an immediately invoked closure
var bar: SomeClass = {
let b = SomeClass()
b.someProperty = "whatever"
b.doSomeStuff()
return b
}()
// ditto for static properties in classes/structures/enums
class MyClass {
static let singleton = MyClass()
init() {
print("foo")
}
}
dispatch_once
を1回初期化で使用していて、何らかの値が得られている場合は、これは素晴らしいことです。初期化するグローバル変数または静的プロパティの値。
しかし、あなたがdispatch_once
を使用して、必ずしも結果をもたらさない仕事をしている場合はどうでしょうか?グローバル変数または静的プロパティでそれを行うことができます:その変数の型をVoid
にするだけです:
let justAOneTimeThing: () = {
print("Not coming back here.")
}()
また、グローバル変数または静的プロパティにアクセスして1回限りの作業を行うのが適切ではない場合、たとえば、クライアントがライブラリを操作する前に「initialize me」関数を呼び出すようにしたい場合は、関数内のアクセス:
func doTheOneTimeThing() {
justAOneTimeThing
}
詳しくは 移行ガイド をご覧ください。
こことインターウェブの周りの他の答えはかなり素晴らしいですが、私はこの小さな情報も言及されるべきだと感じています:
dispatch_once
の素晴らしい点は、最初の実行後にコードを本質的には理解できないほど最適化することでしたが、(実際の)グローバルを設定してチェックするよりもはるかに高速であると確信していますトークン。
トークンの事柄はSwiftで合理的に実装できますが、さらに別の格納されたブール値を宣言する必要はありません。スレッドが安全でないことは言うまでもありません。 doc にあるように、「遅延初期化グローバル」を使用する必要があります。ええ、でもなぜグローバルな範囲を散らかすのですか?
誰かがより良い方法を私に納得させるまで、私はそれを使用するスコープ内で、またはそれに合理的に近い範囲で、次のように一度だけのクロージャーを宣言する傾向があります。
private lazy var foo: Void = {
// Do this once
}()
基本的に、「これを読んだとき、foo
はこのブロックを実行した結果であるべきだ」と言っています。グローバルlet
定数とまったく同じように動作しますが、正しいスコープ内で動作します。そしてきれい。それから、私はそれを他の方法では決して使用されない何かに読みとることによって、好きな場所でそれを呼び出します。私はそのためにSwiftの_
が好きです。そのようです:
_ = foo
この本当にクールな癖は実際にはしばらくの間ありますが、あまり愛を見ていない。基本的に、何かがそのVoid
結果を見たいと思うまで、呼び出されないクロージャーとして実行時に変数をそのままにします。読み取り時に、クロージャーを呼び出して破棄し、その結果をfoo
に保持します。 Void
は実質的にメモリ単位では何も使用しないため、後続の読み取り(つまり_ = foo
)はCPUで何も実行しません。 (それについては引用しないでください。誰かがアセンブリを確認してください!)好きなだけ持って、Swiftは基本的に最初の実行後にそれを気にしなくなります!古いdispatch_once_t
をなくし、クリスマスの日に最初に開いたときと同じくらい多くのコードを保持します。
私の1つの問題は、最初の読み取りの前にfoo
を他の値に設定すると、コードがneverが呼び出されることです!したがって、それを防ぐグローバルlet
定数。つまり、クラススコープの定数はself
でうまく再生されないため、インスタンス変数で再生できません...しかし、真剣に、いつanythingとにかくVoid
に??
それに、戻り値の型をVoid
または()
として指定する必要があります。そうしないと、self
について文句を言うでしょう。誰がサンク?
lazy
は、変数を本来のように遅延させるためのものであるため、Swiftはinit()
で直接実行しません。
あなたがそれに書かないことを覚えている限り、かなりおしゃれです! :P
「lazy var」パターンを使用すると、ディスパッチトークンを気にする必要がなくなり、dispatch_once()
よりも便利になりますが、呼び出しサイトでの表示が気に入らなくなります。
_ = doSomethingOnce
このステートメントは関数呼び出しのように見えますが(アクションを意味するため)、まったくそうではありません。また、結果を明示的に破棄するために_ =
を記述する必要はありません。
より良い方法があります:
lazy var doSomethingOnce: () -> Void = {
print("executed once")
return {}
}()
これにより、次のことが可能になります。
doSomethingOnce()
これはあまり効率的ではないかもしれませんが(Void
を単に破棄するのではなく空のクロージャーを呼び出すため)、改善された明快さは私にとって完全に価値があります。
// Singleton Class
class Singleton: NSObject {
var strSample = NSString()
static let sharedInstance:Singleton = {
let instance = Singleton ()
return instance
} ()
// MARK: Init
override init() {
print("My Class Initialized")
// initialized with variable or property
strSample = "My String"
}
}
// ViewController.Swift
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let mySingleton = Singleton.sharedInstance
print(mySingleton.strSample)
mySingleton.strSample = "New String"
print(mySingleton.strSample)
let mySingleton1 = Singleton.sharedInstance
print(mySingleton1.strSample)
}
My Class Initialized
My String
New String
New String
Xcode 8 GA Swift 3でコンパイル
Dispatch_onceシングルトンクラスインスタンスを作成するための推奨されるエレガントな方法:
final class TheRoot {
static let shared = TheRoot()
var appState : AppState = .normal
...
使用するには:
if TheRoot.shared.appState == .normal {
...
}
これらの行は何をしますか?
final-クラスをオーバーライドしたり、拡張したりすることはできないため、コードの実行速度がやや速くなり、間接性が少なくなります。
static let shared = TheRoot()-この行は遅延initを実行し、一度だけ実行されます。
このソリューションはスレッドセーフです。
移行ガイド :
無料の関数dispatch_onceはSwiftでは使用できなくなりました。 Swiftでは、遅延初期化されたグローバルまたは静的プロパティを使用して、dispatch_onceが提供するものと同じスレッドセーフおよび1回限りの保証を取得できます。
例:
let myGlobal = { … global contains initialization in a call to a closure … }()
// using myGlobal will invoke the initialization code only the first time it is used.
_ = myGlobal