swift 單例的寫法

From my short experience with Swift there are three approaches to implement the Singleton pattern that support lazy initialization and thread safety.

These approaches might change or become redundant as the language matures.

Global constant

private let _SingletonSharedInstance = Singleton()

class Singleton  {
    class var sharedInstance : Singleton {
        return _SingletonSharedInstance
    }
}

We use a global constant because class constants are not yet supported.

This approach supports lazy initialization because Swift lazily initializes global constants (and variables), and is thread safe by virtue of let.

Nested struct

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

Unlike classes, structs do support static constants. By using a nested struct we can leverage its static constant as a class constant.

The nested struct is the approach I recommend until class constants are supported.

dispatch_once

The traditional Objective-C approach ported to Swift. I'm fairly certain there's no advantage over the nested struct approach but I'm putting it here anyway as I find the differences in syntax interesting.

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

Updated with clarifications from Apple

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:

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.

Earlier Solutions

Taking @Krisgellci's answer into account, what I'm going to finally wind up with is what's below. (Ignoring the testing code I used to establish that it seems to be lazy loaded and thread safe) Most of the difference is in using a mechanism that's more consistent and will at some point hopefully allow me to hide the backing store for the global when @private is supported.

class GlobalVariable {
    class var sharedInstance:GlobalVariable {
        return GlobalVariableSharedInstance
    }

    // The rest of this class is merely for testing purposes
    func ping(message:String) {
        NSLog("ping:%@", message)
    }

    init() {
        NSLog("init");
        sleep(5)
    }
}

let GlobalVariableSharedInstance = GlobalVariable()

And the code I used to verify thread-safety and lazy initialization:

class ViewController: UIViewController {

    override func viewDidLoad() {

        NSLog("viewDidLoad")

        super.viewDidLoad()

        for i in 0..5 {
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) {
                NSLog("thread")
                GlobalVariable.sharedInstance.ping("thread")
            }
        }

        GlobalVariable.sharedInstance.ping("viewDidLoad")
    }
}

BTW, using NSLog here instead of println because println isn't thread-safe :)

Apple has clarified that lazy initializer are thread-safe:

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. This enables a cool way to use dispatch_once in your code: just declare a global variable with an initializer and mark it private.


發佈了22 篇原創文章 · 獲贊 0 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章