在Swift中使用dispatch_once單例模型

本文翻譯自:Using a dispatch_once singleton model in Swift

I'm trying to work out an appropriate singleton model for usage in Swift. 我正在嘗試制定一個合適的單例模型以在Swift中使用。 So far, I've been able to get a non-thread safe model working as: 到目前爲止,我已經能夠獲得一個非線程安全模型,其工作方式如下:

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

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

        return Static.instance!
    }
}

Wrapping the singleton instance in the Static struct should allow a single instance that doesn't collide with singleton instances without complex naming schemings, and it should make things fairly private. 在靜態結構中包裝單例實例應該允許在沒有複雜命名方案的情況下不會與單例實例衝突的單個實例,這應該使事情變得相當私有。 Obviously though, this model isn't thread-safe. 但是,顯然,此模型不是線程安全的。 So I tried to add dispatch_once to the whole thing: 所以我試圖將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!
    }
}

But I get a compiler error on the dispatch_once line: 但是我在dispatch_once行上收到編譯器錯誤:

Cannot convert the expression's type 'Void' to type '()' 無法將表達式的類型“無效”轉換爲類型“()”

I've tried several different variants of the syntax, but they all seem to have the same results: 我嘗試了幾種不同的語法變體,但它們似乎都具有相同的結果:

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

What is the proper usage of dispatch_once using Swift? 使用Swift的dispatch_once的正確用法是什麼? I initially thought the problem was with the block due to the () in the error message, but the more I look at it, the more I think it may be a matter of getting the dispatch_once_t correctly defined. 最初,我認爲問題是由於錯誤消息中的()造成的,但是我越看越多,我認爲可能是正確定義dispatch_once_t的問題。


#1樓

參考:https://stackoom.com/question/1cnsj/在Swift中使用dispatch-once單例模型


#2樓

For Swift 1.2 and beyond: 對於Swift 1.2及更高版本:

class Singleton  {
   static let sharedInstance = Singleton()
}

With a proof of correctness (all credit goes here ), there is little to no reason now to use any of the previous methods for singletons. 有了正確性的證明(所有功勞都放在這裏 ),現在幾乎沒有理由對單例使用任何先前的方法。

Update : This is now the official way to define singletons as described in the official docs ! 更新 :這是官方文檔中定義單例的官方方式!

As for concerns on using static vs class . 至於使用static vs class static should be the one to use even when class variables become available. 即使class變量可用, static應該是使用的一種。 Singletons are not meant to be subclassed since that would result in multiple instances of the base singleton. 單例不打算被子類化,因爲這將導致基本單例的多個實例。 Using static enforces this in a beautiful, Swifty way. 使用static以一種美觀,迅捷的方式強制執行此操作。

For Swift 1.0 and 1.1: 對於Swift 1.0和1.1:

With the recent changes in Swift, mostly new access control methods, I am now leaning towards the cleaner way of using a global variable for singletons. 隨着Swift的最新變化(主要是新的訪問控制方法),我現在傾向於使用更簡單的方法來爲單例使用全局變量。

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

As mentioned in the Swift blog article here : 隨着斯威夫特博客文章中提到在這裏

The lazy initializer for a global variable (also for static members of structs and enums) is run the first time that global is accessed, and is launched as dispatch_once to make sure that the initialization is atomic. 全局變量(也適用於結構和枚舉的靜態成員)的惰性初始化程序在首次訪問global時運行,並作爲dispatch_once啓動,以確保初始化是原子的。 This enables a cool way to use dispatch_once in your code: just declare a global variable with an initializer and mark it private. 這提供了一種在代碼中使用dispatch_once的好方法:只需使用初始化程序聲明全局變量並將其標記爲私有即可。

This way of creating a singleton is thread safe, fast, lazy, and also bridged to ObjC for free. 這種創建單例的方法是線程安全,快速,懶惰的,並且還免費橋接到ObjC。


#3樓

Looking at Apple's sample code I came across this pattern. 在查看蘋果的示例代碼時,我遇到了這種模式。 I'm not sure how Swift deals with statics, but this would be thread safe in C#. 我不確定Swift如何處理靜態,但這在C#中是線程安全的。 I include both the property and method for Objective-C interop. 我同時包含了Objective-C互操作的屬性和方法。

struct StaticRank {
    static let shared = RankMapping()
}

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

class var shared:RankMapping {
    return StaticRank.shared
}

#4樓

There is a better way to do it. 有更好的方法可以做到這一點。 You can declare a global variable in your class above the class declaration like this: 您可以在類聲明上方的類中聲明全局變量,如下所示:

var tpScopeManagerSharedInstance = TPScopeManager()

This just calls your default init or whichever init and global variables are dispatch_once by default in Swift. 這只是調用您的默認init,或者默認情況下,Swift中所有init和全局變量都是dispatch_once Then in whichever class you want to get a reference, you just do this: 然後,無論您要獲取參考的哪個類,都可以執行以下操作:

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

So basically you can get rid of the entire block of shared instance code. 因此,基本上,您可以擺脫共享實例代碼的整個塊。


#5樓

Since Apple has now clarified that static struct variables are initialized both lazy and wrapped in dispatch_once (see the note at the end of the post), I think my final solution is going to be: 由於Apple現在已經澄清了靜態結構變量在lazy和包裝在dispatch_once都被初始化(請參閱文章末尾的註釋),所以我認爲我的最終解決方案將是:

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

        return Singleton.instance
    }
}

This takes advantage of the automatic lazy, thread-safe initialization of static struct elements, safely hides the actual implementation from the consumer, keeps everything compactly compartmentalized for legibility, and eliminates a visible global variable. 這利用了靜態結構元素的自動懶惰,線程安全的初始化,對使用者安全地隱藏實際實現,使所有內容緊湊地劃分爲易於辨認並消除了可見的全局變量的優勢。

Apple has clarified that lazy initializer are thread-safe, so there's no need for dispatch_once or similar protections 蘋果已經澄清了惰性初始化器是線程安全的,因此不需要dispatch_once或類似的保護

The lazy initializer for a global variable (also for static members of structs and enums) is run the first time that global is accessed, and is launched as dispatch_once to make sure that the initialization is atomic. 全局變量(也適用於結構和枚舉的靜態成員)的惰性初始化程序在首次訪問global時運行,並作爲dispatch_once啓動,以確保初始化是原子的。 This enables a cool way to use dispatch_once in your code: just declare a global variable with an initializer and mark it private. 這提供了一種在代碼中使用dispatch_once的好方法:只需使用初始化程序聲明全局變量並將其標記爲私有即可。

From here 這裏


#6樓

Use: 使用:

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!
        }
    }
}

How to use: 如何使用:

UtilSingleton.shareInstance.iVal++
println("singleton new iVal = \(UtilSingleton.shareInstance.iVal)")
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章