Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a dispatch_once singleton model in Swift

I'm trying to work out an appropriate singleton model for usage in 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:

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:

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? 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.

like image 347
David Berry Avatar asked Jun 03 '14 20:06

David Berry


People also ask

How do you declare a singleton in Swift?

In Swift, Singleton is a design pattern that ensures a class can have only one object. Such a class is called singleton class. An initializer allows us to instantiate an object of a class. And, making the initializer of a class restricts the object creation of the class from outside of the class.

Is singleton thread safe Ios Swift?

You can create a thread safe singleton using DispatchQueue. We will create a serial queue and add a sync method to set and get value. Below is the code you can achieve thread safe singleton class. It will write an another blog where we can improve the performance using dispatch barrier.

Can singleton class be inherited Swift?

A programmer cannot inherit the pure-singleton class. When you try to inherit any class in swift, you must call the constructor of the superclass.

Is AppDelegate a singleton?

AppDelegate is however a singleton class but you show only use it for declaring things that applies globally in your application. For ex: If you want to change the color of navigation bar in your entire application you can use app delegate and set the color of navigation bar.


2 Answers

tl;dr: Use the class constant approach if you are using Swift 1.2 or above and the nested struct approach if you need to support earlier versions.

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

Class constant

class Singleton  {    static let sharedInstance = Singleton() } 

This approach supports lazy initialization because Swift lazily initializes class constants (and variables), and is thread safe by the definition of let. This is now officially recommended way to instantiate a singleton.

Class constants were introduced in Swift 1.2. If you need to support an earlier version of Swift, use the nested struct approach below or a global constant.

Nested struct

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

Here we are using the static constant of a nested struct as a class constant. This is a workaround for the lack of static class constants in Swift 1.1 and earlier, and still works as a workaround for the lack of static constants and variables in functions.

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

See this GitHub project for unit tests.

like image 66
hpique Avatar answered Oct 11 '22 02:10

hpique


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.

Apple has clarified that lazy initializer are thread-safe, so there's no need for dispatch_once or similar protections

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.

From here

like image 40
6 revs, 2 users 99% Avatar answered Oct 11 '22 03:10

6 revs, 2 users 99%