58

Are the two approaches the same or are there major differences/pitfalls to be aware of:

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

  func helloClass() { print("hello from class Singleton") }
}

struct MyStructSingleton {
  static let sharedInstance = MyStructSingleton()
  private init() {}

  func helloStruct() { print("hello from struct Singleton") }
}
Running Turtle
  • 12,360
  • 20
  • 55
  • 73

3 Answers3

109

The main difference is that class-based mutable singleton works, while struct-based mutable "singleton" doesn't. Unless you want to make your singleton immutable (which is rare) you should stick to the class-based one.

Here is an illustration of how mutable struct-based "singleton" does not work. Consider adding a mutable member state to both singletons, like this:

class MyClassSingleton {
    static let sharedInstance = MyClassSingleton()
    private init(){}
    var state = 5
    func helloClass() { print("hello from class Singleton: \(state)") }
}

struct MyStructSingleton {
    static let sharedInstance = MyStructSingleton()
    private init() {}
    var state = 5
    func helloStruct() { print("hello from struct Singleton: \(state)") }
}

I made state a var, but I could expose it as a read-only property plus a mutating method; the essential thing is that both types are now mutable.

If I do this

let csi = MyClassSingleton.sharedInstance
csi.state = 42
MyClassSingleton.sharedInstance.helloClass()

42 gets printed, because csi is referencing the shared instance.

However, when I do the same thing with struct-based singleton

var ssi = MyStructSingleton.sharedInstance
ssi.state = 42
MyStructSingleton.sharedInstance.helloStruct()

5 gets printed instead, because ssi is a copy of the sharedInstance, which is, of course, an indication that our singleton is not actually a singleton.

Ali Abbas
  • 4,247
  • 1
  • 22
  • 40
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    interestingly, using `static var sharedInstance` instead of `static let sharedInstance` makes it appear to work. Is a new copy of the struct generated every time I call `MyStructSingleton.sharedInstance` ? – Running Turtle Apr 22 '16 at 08:36
  • 3
    @RunningTurtle A new copy of `MyStructSingleton` is created when you declare a new variable of type `MyStructSingleton`. Once you assign `sharedInstance` to it, the two become identical, but they aren't the same instance. The same thing happens when you pass `MyStructSingleton` as a parameter or return it from a method. – Sergey Kalinichenko Apr 22 '16 at 08:45
  • 1
    In fact, you can write a mutable singleton using a struct. You just need to apply all mutations on `sharedInstance` directly and voilà. – Alvae Feb 24 '22 at 02:52
  • @RunningTurtle No, it doesn't – Roman Nov 29 '22 at 06:08
6

That depends on what you want to achieve and how you want to use your structure based on differences between class and struct. Most common thing that you will see is using class with singleton object.

Singletons are pretty much the same, they are only created once, but you will get different behaviors from the class and from struct because:

  • Classes are reference types, while structs are value types
  • Structs are used to define simple structures
  • Structs can't be inherited

There are several more differences but you get the idea from this.

Said Sikira
  • 4,482
  • 29
  • 42
0

As already mentioned in another answer, classes and structs have different properties. Most importantly, classes have reference semantics whereas structs have value semantics.

Nonetheless, a global variable (or a static property) introduces reference semantics, even if it's defined in a struct, suggesting that you can indeed implement a (mutable) singleton with a struct.

The problem illustrated by Sergey Kalinichenko is that assigning the value of a struct to another variable will create a copy rather than a reference (remember, structs have value semantics). So the mutations on the copy won't apply to the original value. Should you mutate the static property of your struct directly, your singleton would indeed be modified.

struct Singleton {

  private init() {}
  var state = 5
  static var sharedInstance = MyStructSingleton()

}

Singleton.sharedInstance.state = 10
print(Singleton.sharedInstance.state) // 10
Alvae
  • 1,254
  • 12
  • 22