Swiftでdispatch_onceシングルトンモデルを使用する


575

Swiftでの使用に適したシングルトンモデルを作成しようとしています。これまでのところ、スレッドセーフでないモデルを次のように機能させることができました。

class var sharedInstance: TPScopeManager {
    get {
        struct Static {
            static var instance: TPScopeManager? = nil
        }

        if !Static.instance {
            Static.instance = TPScopeManager()
        }

        return Static.instance!
    }
}

Static構造体でシングルトンインスタンスをラップすると、複雑な名前付けスキームなしでシングルトンインスタンスと衝突しない単一のインスタンスが許可され、かなりプライベートになるはずです。もちろん、このモデルはスレッドセーフではありません。だから私dispatch_onceは全部に追加しようとしました:

class var sharedInstance: TPScopeManager {
    get {
        struct Static {
            static var instance: TPScopeManager? = nil
            static var token: dispatch_once_t = 0
        }

        dispatch_once(Static.token) { Static.instance = TPScopeManager() }

        return Static.instance!
    }
}

しかし、次のdispatch_once行でコンパイラエラーが発生します。

式のタイプ 'Void'をタイプ '()'に変換できません

構文のさまざまなバリエーションを試しましたが、すべて同じ結果になるようです。

dispatch_once(Static.token, { Static.instance = TPScopeManager() })

dispatch_onceSwiftを使用する適切な使用法は何ですか?最初()はエラーメッセージのに起因する問題がブロックにあると思っていましたが、よく見るほど、dispatch_once_t正しく定義することが問題になると思います。


3
私はすべての静的コードを削除し、@ lazyイニシャライザで読み取り専用プロパティを使用します。
スルタン

1
そういう意味です。残念ながら、内部についてはまだ十分な情報がありません。ただし、IMHOの実装は@lazyスレッドセーフである必要があります。
スルタン

1
この方法には、実装を呼び出し元の捕食にさらさないという利点もあります。
David Berry

1
また、@ lazyクラス変数を使用できるようにも見えません。
David Berry

注意してください!このアプローチで注意すべき2つの点。まず、これから継承するクラスは、sharedInstanceプロパティをオーバーライドする必要があります。Static.instance = TPScopeManager()インスタンスタイプを強制します。Static.instance = self()必要なイニシャライザなどを使用すると、適切な型クラスが生成されます。そうであっても、これは重要なことであり、階層内のすべてのインスタンスに対して一度だけです!初期化する最初のタイプは、すべてのインスタンスに設定されたタイプです。私はobjective-cが同じように振る舞ったとは思いません。
ショーンウッドワード2014年

回答:


713

tl; dr:Swift 1.2以降を使用している場合はクラス定数アプローチを使用し、以前のバージョンをサポートする必要がある場合はネストされた構造体アプローチを使用します。

Swiftでの私の経験から、遅延初期化とスレッドセーフをサポートするシングルトンパターンを実装する3つのアプローチがあります。

クラス定数

class Singleton  {
   static let sharedInstance = Singleton()
}

Swiftはクラス定数(および変数)を遅延初期化し、の定義によりスレッドセーフであるため、このアプローチは遅延初期化をサポートしますlet。これは現在、シングルトンをインスタンス化する方法として公式に推奨されています。

クラス定数はSwift 1.2で導入されました。Swiftの以前のバージョンをサポートする必要がある場合は、以下のネストされたstructアプローチまたはグローバル定数を使用してください。

ネストされた構造体

class Singleton {
    class var sharedInstance: Singleton {
        struct Static {
            static let instance: Singleton = Singleton()
        }
        return Static.instance
    }
}

ここでは、ネストされた構造体の静的定数をクラス定数として使用しています。これは、Swift 1.1以前の静的クラス定数の欠如に対する回避策であり、関数に静的定数と変数の欠如に対する回避策として機能します。

dispatch_once

Swiftに移植された従来のObjective-Cアプローチ。ネストされた構造体のアプローチに勝る利点がないことはかなり確かですが、構文の違いが興味深いので、とにかくここに配置します。

class Singleton {
    class var sharedInstance: Singleton {
        struct Static {
            static var onceToken: dispatch_once_t = 0
            static var instance: Singleton? = nil
        }
        dispatch_once(&Static.onceToken) {
            Static.instance = Singleton()
        }
        return Static.instance!
    }
}

単体テストについては、このGitHubプロジェクトを参照しください。


13
「letのおかげでスレッドセーフ」—これはどこかで述べられていますか?ドキュメントでそれについての言及を見つけることができません。
jtbandes 2014年

4
@jtbandes定数は、私が知っているすべての言語でスレッドセーフです。
hpique 2014年

2
@DaveWood最後のアプローチについて話していると思います。「このアプローチを使用する必要はなくなったと思いますが、構文の違いが興味深いので、とにかくここに配置します。」
hpique 14年

5
initまたprivate、オブジェクトの1つのインスタンスのみがアプリの存続期間を通じて存在することを保証するように宣言する必要がありますか?
Andrew

5
「クラス定数」アプローチでは、(a)クラスをfinalサブクラス化しないように宣言することをお勧めします。(b)initメソッドを宣言して、private誤って別のインスタンスをどこかにインスタンス化しないようにします。
ロブ

175

Appleが静的構造体変数が遅延初期化されてラップされることを明確にしたのでdispatch_once(投稿の最後の注を参照)、私の最終的な解決策は次のようになると思います:

class WithSingleton {
    class var sharedInstance: WithSingleton {
        struct Singleton {
            static let instance = WithSingleton()
        }

        return Singleton.instance
    }
}

これは、静的なstruct要素の自動遅延およびスレッドセーフな初期化を利用し、実際の実装をコンシューマーから安全に隠し、すべてをコンパクトに区分けして読みやすくし、目に見えるグローバル変数を排除します。

Appleはレイジーイニシャライザーがスレッドセーフであることを明確にしたので、dispatch_onceまたは同様の保護は必要ありません

グローバル変数のレイジー初期化子(構造体と列挙型の静的メンバーも同様)は、最初にグローバルにアクセスしたときに実行され、dispatch_onceとして起動され、初期化がアトミックであることを確認します。これにより、コードでdispatch_onceを使用するクールな方法が可能になります。初期化子を使用してグローバル変数を宣言し、プライベートにマークするだけです。

ここから


1
確認するには:グローバル変数には遅延のあるスレッドセーフな初期化がありますが、クラス変数にはありません。正しい?
Bill

14
private init() {}このクラスが外部でインスタンス化されることを意図していないという事実をさらに強化するために、イニシャライザをプライベートとして宣言することをお勧めします。
Pascal Bourque 2014

1
静的構造体変数の初期化はレイジーでスレッドセーフです。その静的構造体変数がマルチトンのディクショナリである場合、アクセスごとに呼び出しを手動で同期/キューイングする必要があります。

私があなたの質問を正しく理解している場合、辞書および配列へのアクセスは本質的にスレッドセーフではないため、何らかの形のスレッド同期を使用する必要があります。
David Berry

@DavidBerryこのシングルトンクラス内で関数を呼び出すにはどうすればよいですか。myClass.sharedInstanceの最初の呼び出しで呼び出される関数が必要です。
Ameet Dhas

163

Swift 1.2以降の場合:

class Singleton  {
   static let sharedInstance = Singleton()
}

正しさの証明(すべてのクレジットはここにあります)により、シングルトンに対して以前の方法のいずれかを使用する理由はほとんどありません。

更新:これは、公式ドキュメントで説明されているように、シングルトンを定義する公式の方法です。

staticvsの使用に関する懸念についてはclass。変数が使用可能になったstatic場合でも使用する必要がclassあります。シングルトンは、ベースシングルトンの複数のインスタンスが生成されるため、サブクラス化することを意図していません。を使用staticすると、これが美しく、迅速に実行されます。

Swift 1.0および1.1の場合:

Swiftの最近の変更、主に新しいアクセス制御メソッドにより、私は今、シングルトンにグローバル変数を使用するよりクリーンな方法に傾倒しています。

private let _singletonInstance = SingletonClass()
class SingletonClass {
  class var sharedInstance: SingletonClass {
    return _singletonInstance
  }
}

ここのSwiftブログ記事述べたように:

グローバル変数のレイジー初期化子(構造体と列挙型の静的メンバーも同様)は、最初にグローバルにアクセスしたときに実行され、dispatch_onceとして起動され、初期化がアトミックであることを確認します。これにより、コードでdispatch_onceを使用するクールな方法が可能になります。初期化子を使用してグローバル変数を宣言し、プライベートにマークするだけです。

シングルトンを作成するこの方法は、スレッドセーフ、高速、遅延であり、無料でObjCにブリッジすることもできます。


2
この答えだけを読んでいる人:トークンを静的にすることを忘れないでください。そうしないと、動作が未定義になります。完全なコードについては、Davidが編集した質問を参照してください。
nschum 2014年

それ以外の場合、@ nschum、動作は未定義ではなく、明確に定義された方法で壊れているだけです。ブロックは常に実行されます。
Michael

@Michael:ドキュメントには未定義と記載されています。したがって、現在の動作は偶然です。
nschum 2014年

1
それは奇妙なことです。ドキュメンテーションで「未定義」と呼ばれている場合、それは、コードを書いた人がその機能について何の約束もしないことを意味します。変数が静的かどうかを知るコードとは関係ありません。これは、現在の(または明らかな)動作に依存できないことを意味します。
nschum 2014年

6
private init() {}イニシャライザとして追加することができますSingletonClass。外部からのインスタンス化を防ぐため。
rintaro

46

Swift 1.2以降では、クラスの静的変数/定数がサポートされるようになりました。したがって、静的定数を使用することができます:

class MySingleton {

    static let sharedMySingleton = MySingleton()

    private init() {
        // ...
    }
}

35

それを行うためのより良い方法があります。次のように、クラス宣言の上のクラスでグローバル変数を宣言できます。

var tpScopeManagerSharedInstance = TPScopeManager()

これは、デフォルトのinit、またはdispatch_onceSwiftのデフォルトのinitおよびグローバル変数を呼び出すだけです。次に、参照を取得するクラスで、次のようにします。

var refrence = tpScopeManagerSharedInstance
// or you can just access properties and call methods directly
tpScopeManagerSharedInstance.someMethod()

したがって、基本的には共有インスタンスコードのブロック全体を取り除くことができます。


3
なぜ「var」と「let」がたくさんあるのですか?
Stephan

1
たぶんletかもしれない、私はvarでそれをテストしただけです。
Kris Gellci 2014

私はこの答えが好きですが、Interface Builderからこれ(シングルトン)にアクセスする必要があります。IB内からこのtpScopeManagerSharedInstanceにアクセスするにはどうすればよいですか?ありがとう。-–
Luis Palacios

これは、シングルトンを持つための私の好ましい方法です。それはすべての通常の機能(スレッドセーフ&怠惰なインスタンス化を)持っていない、それは非常に軽量な構文をサポートしています記述する必要TPScopeManager.sharedInstance.doIt()すべての時間を、ちょうどあなたのクラスに名前を付けTPScopeManagerClass、次の授業にこの宣言を持っているpublic let TPScopeManager = TPScopeManagerClass()、とだけ書き込みを使用した場合TPScopeManager.doIt()。非常にきれいな!
Alex

ここにはTPScopeManager、の追加インスタンスの作成を妨げるものは何もないため、定義上はシングルトンではありません
Caleb 2016年

28

スウィフトのシングルトンは、クラスの機能、例えばとしてCocoaフレームワークで公開されていますNSFileManager.defaultManager()NSNotificationCenter.defaultCenter()。したがって、他のいくつかのソリューションのようにクラス変数ではなく、クラスの関数としてこの動作をミラー化する方が理にかなっています。例えば:

class MyClass {

    private static let _sharedInstance = MyClass()

    class func sharedInstance() -> MyClass {
        return _sharedInstance
    }
}

を介してシングルトンを取得しMyClass.sharedInstance()ます。


1
LearnCocos2Dのコメントに賛成:)、スタイルについても。
x4h1d

2
グローバル変数は、クラス内のstaticを介してクラス変数に変更する必要があります。
Malhal

2
@malhal変数がプライベートとマークされているがクラスの外にある場合、それはグローバルではありません-スコープはそれが含まれているファイルに限定されます。クラス内の静的はほとんど同じように機能しますが、静的を使用するように答えを更新しましたあなたが提案したように、ファイル内で複数のクラスをたまたま使用した場合は、変数をクラスにグループ化する方が良いでしょう。
ライアン

1
「Swiftシングルトンは、クラス関数としてcocoaフレームワークで公開されています」... Swift 3にはありません。現在、通常はstaticプロパティです。
2016

17

Appleのドキュメントによれば、Swiftでこれを行う最も簡単な方法は静的型プロパティを使用することです。

class Singleton {
    static let sharedInstance = Singleton()
}

ただし、単純なコンストラクター呼び出し以外の追加のセットアップを実行する方法を探している場合、秘密はすぐに呼び出されるクロージャーを使用することです。

class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code
        return instance
    }()
}

これはスレッドセーフであることが保証されており、遅延初期化は1回だけです。


静的letインスタンスをnilに戻すにはどうすればよいですか?
gpichler 2015年

1
@ user1463853-できません。通常はできません。
2016

16

スウィフト4+

protocol Singleton: class {
    static var sharedInstance: Self { get }
}

final class Kraken: Singleton {
    static let sharedInstance = Kraken()
    private init() {}
}

2
これには最終クラスが必要です、違いをもっと説明できますか、構造体を使用したシングルトンの他のソリューションで問題があります
Raheel Sadiq

プライベートオーバーライドする必要がありますinit(){}
NSRover

8

Appleのサンプルコードを見ると、このパターンに出くわしました。Swiftがstaticをどのように処理するかはわかりませんが、これはC#ではスレッドセーフになります。Objective-C相互運用のプロパティとメソッドの両方を含めます。

struct StaticRank {
    static let shared = RankMapping()
}

class func sharedInstance() -> RankMapping {
    return StaticRank.shared
}

class var shared:RankMapping {
    return StaticRank.shared
}

このデフォルトの静的構文を使用するだけですべての迷惑な仕事ができると確信しています。
Eonil、2014年

残念ながら、staticsは構造体の内部でのみ機能するため、このパターンを使用します。
user2485100 2014年

私の意図は、私たちはものを使う必要がないということでしたdispatch_once。私はあなたのスタイルに賭けています。:)
Eonil 2014年

classクラス宣言内でstaticは、構造体宣言と同等ではありませんか?
Russell Borogove、2014年

@サムはい、そうです。ファイルと初期化に関するAppleのブログエントリを参照してくださいdispatch_once。構造体と列挙型のグローバルと静的メンバーの両方がこの機能の恩恵を受けることを明確にしています。
Rob

5

簡単に言えば、

class Manager {
    static let sharedInstance = Manager()
    private init() {}
}

ファイルと初期化を読むことをお勧めします

グローバル変数のレイジー初期化子(構造体と列挙型の静的メンバーも)は、最初にグローバルにアクセスしたときに実行されdispatch_once、初期化がアトミックであることを確認するために起動されます。


4

Objective-CでSwiftシングルトンクラスを使用することを計画している場合、この設定により、コンパイラーは適切なObjective-Cのようなヘッダーを生成します。

class func sharedStore() -> ImageStore {
struct Static {
    static let instance : ImageStore = ImageStore()
    }
    return Static.instance
}

次に、Objective-Cクラスでは、Swift以前の時代と同じ方法でシングルトンを呼び出すことができます。

[ImageStore sharedStore];

これは私の単純な実装です。


これは、他のSwiftシングルトンと同じ方法で実装されているため、実際には他の例よりも簡潔で正しいです。つまり、のようなクラス関数としてNSFileManager.defaultManager()使用されますが、Swiftの遅延スレッドセーフな静的メンバーメカニズムを使用します。
レスリーゴドウィン

Cocoaは通常、これらをクラス関数としてではなく、静的プロパティとして実装します。
Rob

私はそれを知っています。私のコメントは2年以上前のものです。言及いただきありがとうございます。
マイケル

4

最初の解決策

let SocketManager = SocketManagerSingleton();

class SocketManagerSingleton {

}

コードの後半:

func someFunction() {        
    var socketManager = SocketManager        
}

第二の解決策

func SocketManager() -> SocketManagerSingleton {
    return _SocketManager
}
let _SocketManager = SocketManagerSingleton();

class SocketManagerSingleton {

}

コードの後半では、混乱を少なくするために中括弧を付けることができます。

func someFunction() {        
    var socketManager = SocketManager()        
}

4
final class MySingleton {
     private init() {}
     static let shared = MySingleton()
}

次に、それを呼び出します。

let shared = MySingleton.shared

initasとしてマークするだけでなくprivatesharedMyModelas を作成することもできfinalます。将来の読者のために、Swift 3では、sharedMyModel単にに名前を変更する傾向があるかもしれませんshared
Rob

これが唯一の正しい答えです。ただし、オーバーライドとsuper.initの呼び出しは誤っており、コンパイルすらできません。
マイケル・モリス

4

使用する:

class UtilSingleton: NSObject {

    var iVal: Int = 0

    class var shareInstance: UtilSingleton {
        get {
            struct Static {
                static var instance: UtilSingleton? = nil
                static var token: dispatch_once_t = 0
            }
            dispatch_once(&Static.token, {
                Static.instance = UtilSingleton()
            })
            return Static.instance!
        }
    }
}

使い方:

UtilSingleton.shareInstance.iVal++
println("singleton new iVal = \(UtilSingleton.shareInstance.iVal)")

これは、現在の回答への途中で私が経験した回答の1つとまったく同じです。グローバル変数は遅延とスレッドセーフの両方で初期化されるため、複雑さが増す理由はありません。
David Berry

@Davidグローバル変数がないこと以外。:)
hpique 2014年

@hpiqueいいえ、私の以前の試みの1つとまったく同じです。編集履歴を見てください。
David Berry


3

Apple Docs(Swift 3.0.1)から、

静的タイププロパティを使用するだけで、複数のスレッドに同時にアクセスした場合でも、1回だけ遅延して初期化されることが保証されます。

class Singleton {
    static let sharedInstance = Singleton()
}

初期化以外の追加のセットアップを実行する必要がある場合は、クロージャーの呼び出しの結果をグローバル定数に割り当てることができます。

class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code
        return instance
    }()
}

3

私はお勧めしenumますが、Javaの、例えばで使用するように、

enum SharedTPScopeManager: TPScopeManager {
    case Singleton
}

IMO、これがシングルトンを実装する唯一の正しいSwiftの方法です。他の答えはObjC / C / C ++の方法です
ブライアンチェン

この答えについて詳しく教えてください。シングルトンがこのスニペットからインスタンス化される場所は私には明らかではありません
ケニーウィンカー

@KennyWinker私はApple開発者ログインを持っていないため、迅速ではないため、初期化が発生したときに応答できません。Javaでは、初めて使用されます。おそらく、初期化時に印刷を試して、起動時またはアクセス後に印刷が発生するかどうかを確認できます。これは、列挙型がコンパイラーによってどのように実装されるかによって異なります。
ハワードロバット14

@KennyWinkler:Appleはこれがどのように機能するかを明確にしたところです。developer.apple.com/ swift / blog /?id = 7を参照してください。特に、「Javaのように、最初に参照されるときにグローバルの初期化子を実行する」と述べています。彼らはまた、内部では「dispatch_onceを使用して、初期化がアトミックであることを確認している」とも語っています。したがって、列挙型はほとんど確実にあなたがやる気のある初期化をしない限り行く方法です、そして私的な静的なletが解決策です。
ハワードロバット2014

2

参考までに、ここにJack Wu / hpiqueのNested Struct実装のシングルトン実装の例を示します。実装は、アーカイブがどのように機能するか、およびいくつかの付随する機能も示しています。私はこれの完全な例を見つけることができなかったので、うまくいけばこれが誰かを助けるでしょう!

import Foundation

class ItemStore: NSObject {

    class var sharedStore : ItemStore {
        struct Singleton {
            // lazily initiated, thread-safe from "let"
            static let instance = ItemStore()
        }
        return Singleton.instance
    }

    var _privateItems = Item[]()
    // The allItems property can't be changed by other objects
    var allItems: Item[] {
        return _privateItems
    }

    init() {
        super.init()
        let path = itemArchivePath
        // Returns "nil" if there is no file at the path
        let unarchivedItems : AnyObject! = NSKeyedUnarchiver.unarchiveObjectWithFile(path)

        // If there were archived items saved, set _privateItems for the shared store equal to that
        if unarchivedItems {
            _privateItems = unarchivedItems as Array<Item>
        } 

        delayOnMainQueueFor(numberOfSeconds: 0.1, action: {
            assert(self === ItemStore.sharedStore, "Only one instance of ItemStore allowed!")
        })
    }

    func createItem() -> Item {
        let item = Item.randomItem()
        _privateItems.append(item)
        return item
    }

    func removeItem(item: Item) {
        for (index, element) in enumerate(_privateItems) {
            if element === item {
                _privateItems.removeAtIndex(index)
                // Delete an items image from the image store when the item is 
                // getting deleted
                ImageStore.sharedStore.deleteImageForKey(item.itemKey)
            }
        }
    }

    func moveItemAtIndex(fromIndex: Int, toIndex: Int) {
        _privateItems.moveObjectAtIndex(fromIndex, toIndex: toIndex)
    }

    var itemArchivePath: String {
        // Create a filepath for archiving
        let documentDirectories = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
        // Get the one document directory from that list
        let documentDirectory = documentDirectories[0] as String
        // append with the items.archive file name, then return
        return documentDirectory.stringByAppendingPathComponent("items.archive")
    }

    func saveChanges() -> Bool {
        let path = itemArchivePath
        // Return "true" on success
        return NSKeyedArchiver.archiveRootObject(_privateItems, toFile: path)
    }
}

そして、これらの機能の一部を認識しなかった場合は、ここで私が使用してきた生きているSwiftユーティリティファイルを少し示します。

import Foundation
import UIKit

typealias completionBlock = () -> ()

extension Array {
    func contains(#object:AnyObject) -> Bool {
        return self.bridgeToObjectiveC().containsObject(object)
    }

    func indexOf(#object:AnyObject) -> Int {
        return self.bridgeToObjectiveC().indexOfObject(object)
    }

    mutating func moveObjectAtIndex(fromIndex: Int, toIndex: Int) {
        if ((fromIndex == toIndex) || (fromIndex > self.count) ||
            (toIndex > self.count)) {
                return
        }
        // Get object being moved so it can be re-inserted
        let object = self[fromIndex]

        // Remove object from array
        self.removeAtIndex(fromIndex)

        // Insert object in array at new location
        self.insert(object, atIndex: toIndex)
    }
}

func delayOnMainQueueFor(numberOfSeconds delay:Double, action closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue()) {
            closure()
    }
}

2

迅速に、次の方法でシングルトンクラスを作成できます:

class AppSingleton: NSObject {

    //Shared instance of class
    static let sharedInstance = AppSingleton()

    override init() {
        super.init()
    }
}

1

私はこの実装を好みます:

class APIClient {

}

var sharedAPIClient: APIClient = {
    return APIClient()
}()

extension APIClient {
    class func sharedClient() -> APIClient {
        return sharedAPIClient
    }
}

1

Swiftでの私の実装方法...

ConfigurationManager.swift

import Foundation

    let ConfigurationManagerSharedInstance = ConfigurationManager()
 class ConfigurationManager : NSObject {
    var globalDic: NSMutableDictionary = NSMutableDictionary()

class var sharedInstance:ConfigurationManager {
    return ConfigurationManagerSharedInstance

}

init() {

    super.init()

    println ("Config Init been Initiated, this will be called only onece irrespective of many calls")   

}

以下の方法で、アプリケーションの任意の画面からglobalDicにアクセスします。

読んだ:

 println(ConfigurationManager.sharedInstance.globalDic)  

書く:

 ConfigurationManager.sharedInstance.globalDic = tmpDic // tmpDict is any value that to be shared among the application

1

唯一正しい方法は以下のとおりです。

final class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code if anything
        return instance
    }()

    private init() {}
}

アクセスするために

let signleton = Singleton.sharedInstance

理由:

  • static typeプロパティは、複数のスレッドで同時にアクセスされた場合でも、1回だけ遅延して初期化されることが保証されているため、使用する必要はありません。 dispatch_once
  • initメソッドをプライベート化して、インスタンスが他のクラスによって作成されないようにします。
  • final 他のクラスがシングルトンクラスを継承しないようにするためです。

なぜ直接使用できるのにクロージャーの初期化を使用したのですかstatic let sharedInstance = Singleton()
abhimuralidharan

1
追加の設定をしたくない場合は、あなたが言っていることが正しいです。
applefreak

1

Davidの実装を見ると、クラスメソッドとほとんど同じことをしているinstanceMethodので、シングルトンクラス関数を持っている必要はないようです。必要なことは、それをグローバル定数として宣言することだけです。letsharedInstance

let gScopeManagerSharedInstance = ScopeManager()

class ScopeManager {
   // No need for a class method to return the shared instance. Use the gScopeManagerSharedInstance directly. 
}

2
コメントで述べたように、これを行う唯一の理由は、将来のある時点でグローバル変数を移動/非表示にして、よりシングルトンのような動作を実現できることです。その時点で、すべてが一貫したパターンを使用している場合は、使用方法を変更する必要なく、シングルトンクラス自体を変更できます。
David Berry

0
   func init() -> ClassA {
    struct Static {
        static var onceToken : dispatch_once_t = 0
        static var instance : ClassA? = nil
    }

    dispatch_once(&Static.onceToken) {
        Static.instance = ClassA()
    }

    return Static.instance!
}

ここで詳しく説明したようにdispatch_once、静的変数の初期化はレイジーであり、dispatch_once Apple を介して自動的に保護されるため、dispatch_onceではなくstaticsを使用することが推奨されるため、初期化をラップする必要はありません。
David Berry、

0

過去にシングルトンを実現するためのSwiftは、グローバル変数、内部変数、およびdispatch_onceの3つの方法にすぎません。

ここに2つの良いシングルトンがあります(注:どんな種類の書き込みでも、プライベート化のinit()メソッドに注意を払う必要があります.Swiftでは、オブジェクトのコンストラクターのデフォルトはすべてパブリックなので、書き換える必要がありますinitをプライベートに変えることができます、デフォルトの初期化メソッドによってこのクラス '()'の他のオブジェクトがオブジェクトを作成するのを防ぎます。)

方法1:

class AppManager {
    private static let _sharedInstance = AppManager()

    class func getSharedInstance() -> AppManager {
       return _sharedInstance
    }

    private init() {} // Privatizing the init method
}

// How to use?
AppManager.getSharedInstance()

方法2:

class AppManager {
    static let sharedInstance = AppManager()

    private init() {} // Privatizing the init method
}

// How to use?
AppManager.sharedInstance

-1

これは、スレッドセーフ機能を備えた最も単純なものです。他のスレッドは、必要な場合でも同じシングルトンオブジェクトにアクセスできません。 スイフト3/4

struct DataService {

    private static var _instance : DataService?

    private init() {}   //cannot initialise from outer class

    public static var instance : DataService {
        get {
            if _instance == nil {
                DispatchQueue.global().sync(flags: .barrier) {
                    if _instance == nil {
                        _instance = DataService()
                    }
                }
            }
            return _instance!
        }
    }
}

2
静的な型のプロパティ(複数のスレッドで同時にアクセスされた場合でも、1回だけ遅延して初期化されることが保証されている)の利点は何ですか?
マーティンR

-1

シングルトンに継承を許可するように要求しましたが、これらの解決策のいずれも実際には継承を許可していませんでした。だから私はこれを思いつきました:

public class Singleton {
    private static var sharedInstanceVar = Singleton()

    public class func sharedInstance() -> Singleton {
        return sharedInstanceVar
    }
}


public class SubSingleton: Singleton {

    private static var sharedInstanceToken: dispatch_once_t = 0

    public class override func sharedInstance() -> SubSingleton {
        dispatch_once(&sharedInstanceToken) {
            sharedInstanceVar = SubSingleton()
        }
    return sharedInstanceVar as! SubSingleton
    }
}
  • このように、Singleton.sharedInstance()最初に実行すると、インスタンスが返されますSingleton
  • やったときSubSingleton.sharedInstance()最初にそれはのインスタンスを返しますSubSingleton作成します。
  • 上記のようにすれば、その後SubSingleton.sharedInstance()Singleton真であり、同じインスタンスが使用されます。

この最初のダーティなアプローチの問題は、サブクラスがを実装し、dispatch_once_tそれsharedInstanceVarがクラスごとに1回だけ変更されることを保証できないことです。

私はこれをさらに改良しようと試みますが、誰かがこれに対して強い感情を持っているかどうかを確認するのは興味深いことです(冗長であり、手動で更新する必要があるという事実以外に)。



-2

次の構文を使用します。

public final class Singleton {    
    private class func sharedInstance() -> Singleton {
        struct Static {
            //Singleton instance.
            static let sharedInstance = Singleton()
        }
        return Static.sharedInstance
    }

    private init() { }

    class var instance: Singleton {
        return sharedInstance()
    }
}

これはSwift 1.2から4まで機能し、いくつかの利点があります。

  1. 実装をサブクラス化しないようにユーザーに思い出させる
  2. 追加のインスタンスの作成を防ぎます
  3. 遅延作成と一意のインスタンス化を保証します
  4. 次のようにインスタンスへのアクセスを許可することにより、構文を短縮します(()を回避します) Singleton.instance
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.