56

さて、Xcode 8 の新しいSwifty Dispatch APIについて知りました。私は を使って楽しんでおり、Xcode のモジュールをDispatchQueue.main.asyncブラウズしてすべての新しい API を見つけました。Dispatch

しかしdispatch_once、シングルトンの作成や 1 回限りのセットアップなどが (マルチスレッド環境であっても) 2 回以上実行されないようにするためにも使用しています...そして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.")
    }
}
4

7 に答える 7

58

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
}

詳細については、移行ガイドを参照してください。

于 2016-06-14T01:02:49.890 に答える
17

ここおよびインターウェブに関する他の回答は非常に優れていますが、このちょっとした情報も言及する必要があると思います。

素晴らしい点dispatch_onceは、それがどれほど最適化されたかということでした。基本的に、最初の実行後にほとんど理解できない方法でコードを修正しましたが、(実際の) グローバル トークンを設定してチェックするよりもはるかに高速であると合理的に確信しています。

トークンは Swift で合理的に実装できますが、さらに別の格納されたブール値を宣言する必要があるのはそれほど素晴らしいことではありません。スレッドセーフではないことは言うまでもありません。ドキュメントにあるように、「遅延初期化されたグローバル」を使用する必要があります。ええ、しかし、なぜグローバル スコープをごちゃごちゃにしていますか?

誰かがより良い方法を私に納得させるまで、次のように、使用するスコープ内、またはそれにかなり近いスコープ内で do-once クロージャーを宣言する傾向があります。

private lazy var foo: Void = {
    // Do this once
}()

foo基本的には、「これを読んだとき、このブロックを実行した結果になるはずだ」と言っています。letこれは、グローバル定数とまったく同じように、適切な範囲で動作します。そしてきれい。次に、他の方法では決して使用されないものに読み込んで、好きな場所で呼び出します。そういう意味ではSwiftが好き_です。そのようです:

_ = foo

この本当にクールな癖は、実際にはしばらく前から存在していましたが、あまり愛されていません. Void基本的に、何かがその結果を見たいと思うまで、呼び出されていないクロージャーとして、実行時に変数をそのままにしておきます。読み取り時に、クロージャを呼び出して破棄し、その結果を に保持しfooます。Voidメモリに関しては実質的に何も使用しないため、後続の読み取り (つまり_ = foo) は CPU で何もしません。(それについて私を引用しないでください。誰かが確実にアセンブリをチェックしてください!) 好きなだけ持っていれば、Swift は基本的に最初の実行後にそれを気にするのをやめます! その古いdispatch_once_t.

私の1つの問題はfoo、最初の読み取りの前に別のものに設定でき、コードが呼び出されないことです! したがって、それを防ぐグローバルlet定数です。つまり、クラス スコープ内の定数は でうまく機能しないselfため、インスタンス変数をいじることはできません...しかし、真剣に、とにかくいつ何かを設定しVoidますか??

それと、戻り値の型をVoidorとして指定する必要があります。誰だ?()self

そしてlazy、変数をできるだけ遅延させるだけなので、Swift はそれを on で直接実行しませんinit()

あなたがそれに書いていないことを覚えている限り、かなりおしゃれです!:P

于 2016-09-16T22:52:31.037 に答える
8

Swift 3.0 での「dispatch_once」の例

ステップ1:以下のコードをSingleton.swift(Singletonクラス)に置き換えるだけです

// 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"
}
}

シングルトン サンプル イメージ

ステップ 2 : ViewController.swift からシングルトンを呼び出す

// 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)

    }

ViewController サンプル画像

このような出力

My Class Initialized
My String
New String
New String
于 2017-01-05T08:18:52.773 に答える