web-dev-qa-db-ja.com

Swiftのdispatch_onceの例

Swiftでdispatch_onceを使用する方法の例はありますか? (できればAppleからのものが望ましいです。)

注:この場合、私は シングルトンには使用しません ;任意のコードを1回だけ実行したい。

更新:これをインスタンスメソッドで使用するときに推奨される規則に主に関心がありますが、クラスメソッド、関数、およびグローバルでの使用コンテキストは完全性のために役立ちます。

13
Senseful

dispatch_once_tはタイプエイリアス(Int)です。ヘッダードキュメント:

/*!
 * @typedef dispatch_once_t
 *
 * @abstract
 * A predicate for use with dispatch_once(). It must be initialized to zero.
 * Note: static and global variables default to zero.
 */
typealias dispatch_once_t = Int

そして、これがdispatch_onceドキュメントからの引用です。

述部は、グローバルスコープまたは静的スコープに格納されている変数を指している必要があります。自動または動的ストレージ(Objective-Cインスタンス変数を含む)で述語を使用した結果は未定義です。

トークン変数はグローバル/静的スコープに格納する必要があり、ゼロに初期化する必要があります。これにより、次のコードが生成されます。

import Foundation

var token: dispatch_once_t = 0
dispatch_once(&token) { () -> Void in
  print("Called once")
}

= 0token初期化)を省略すると、コンパイラーがエラー初期化前に取得した変数「トークン」のアドレスを生成するため、機能しません静的およびグローバルがデフォルトでゼロであるという事実にもかかわらず。 Xcode7B2でテスト済み。


コメントに基づくその他の例。 classメソッド内にいる場合は、いくつかの可能性があります。

メソッド内で静的プロパティを宣言することはできません。そうしないと、コンパイラが静的プロパティを宣言できるのはtypeエラーの場合のみです。これは機能しません:

class func doItOnce() {
  static var token: dispatch_once_t = 0
  ...
}

型で宣言する必要があります。これはSwift 1.2(Xcode 6.3 IIRC)で導入されました。

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

では、グローバルが気に入らない場合はどうすればよいでしょうか。

型の静的変数

class MyClass {
  private static var token: dispatch_once_t = 0

  class func doItOnce() {
    dispatch_once(&token) {
      print("Do it once")
    }
  }
}

構造体でラップされたメソッドの静的

Yurクラスの静的プロパティが好きではありませんか?あなたの方法でそれを持ちたいですか?次のような構造体でラップします。

class func doItOnce() {
  struct Tokens { static var token: dispatch_once_t = 0 }
  dispatch_once(&Tokens.token) {
    print("Do it once")
  }
}

実際、私はAppleの推奨事項、ベストプラクティス、... dispatch_onceの方法を知りません。あなたが最も好きなものを使用するだけで、気分が良く、ただグローバル/静的スコープの基準を満たします。

28
zrzka

好奇心旺盛な方にとって、私にとってこのアプローチはこの目的に役立ちました。

class SomeVC : UIViewController {
    private var token: dispatch_once_t = 0

    public override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        dispatch_once(&token) { () -> Void in
            self.doSomethingOnce()
        }

    }
}

静的変数を宣言しないことにより、期待される動作が得られます。そうは言っても、これは深刻なプロジェクトには絶対にお勧めできません。ドキュメントには(よく言われているように)次のように記載されているからです。

述部は、グローバルスコープまたは静的スコープに格納されている変数を指している必要があります。自動または動的ストレージ(Objective-Cインスタンス変数を含む)で述語を使用した結果は未定義です。

将来の奇妙なバグや未定義の振る舞いに遭遇したくない場合は、Appleの言うことだけに固執します。しかし、これらのことをいじってみるのはいいですね。 =)

4
Robertibiris