Using the Swift Singleton
There is a lot of info available on singletons in Swift. Have you come across this article with your Google prowess? http://krakendev.io/blog/the-right-way-to-write-a-singleton
But to answer your question, you can simply define anything you'd like to use normally.
class Singleton {
static let sharedInstance = Singleton() // this makes singletons easy in Swift
var stringArray = [String]()
}
let sharedSingleton = Singleton.sharedInstance
sharedSingleton.stringArray.append("blaaaah") // ["blaaaah"]
let anotherReferenceToSharedSingleton = Singleton.sharedInstance
print(anotherReferenceToSharedSingleton.stringArray) // "["blaaaah"]\n"
How to use the singleton pattern in conjunction with dependency injection?
Could you show me how to properly use dependency injection with the singleton pattern in Swift?
Rather than accessing
ServiceSingleton.shared
directly, you access an instance variable that is injected into your object, usually in the initializer if possible, otherwise as a settable property, post-initialization:protocol FooService {
func doFooStuff()
}
class ProductionFooService: FooService {
private init() {}
static let shared = ProductionFooService()
func doFooStuff() {
print("real URLSession code goes here")
}
}
struct MockFooService: FooService {
func doFooStuff() {
print("Doing fake foo stuff!")
}
}
class FooUser {
let fooService: FooService
init(fooService: FooService) { // "initializer based" injection
self.fooService = fooService
}
func useFoo() {
fooService.doFooStuff() // Doesn't directly call ProductionFooService.shared.doFooStuff
}
}
let isRunningInAUnitTest = false
let fooUser: FooUser
if !isRunningInAUnitTest {
fooUser = FooUser(fooService: ProductionFooService.shared) // In a release build, this is used.
}
else {
fooUser = FooUser(fooService: MockFooService()) // In a unit test, this is used.
}
fooUser.useFoo()Typically initialization of ViewControllers is done by your storyboards, so you can't ingect your dependancies via initializer parameters, and will have to instead use stored properties that are set after object initialization.
Could you explain to me what this achieves?
Your code is no longer coupled to
ProductionFooService.shared
. As a result of this, you can introduce different implementations ofFooService
, such as one for a beta environment, a mock one for unit testing, etc.If all your code pervasively directly uses your prod dependancies, you'll...
find that it's impossible to instantiate your objects in a test environment. You don't want your unit tests, CI test environments, beta environments, etc. connecting to prod databases, services and APIs.
Have no true "unit" tests. Every test will be testing a unit of code, plus all of the common dependancies that it transitively depends on. If you were to ever make a code change to one of these dependancies, it would break most of the unit tests in your system, which makes it harder to pin down exactly what failed. By decoupling your dependancies, you can use mock objects that do the bare minimum necessary to support a unit test, and ensure that each test is only testing a particular unit of code, and not the transitive dependancies it relies on.
Should I always use DI when I use the singleton pattern in my iOS projects from now on?
It's a good habit to pick up. Of course, there are qucik-and-dirty-projects for which you just want to move fast and won't really care, but it'll surprise you how many of these supposed qucik-and-dirty-projects actually take off, and pay the cost down the road. You just need to be cognizant of when you're hindering yourself by not taking some extra time to decouple your decencies.
Any reason not use use a singleton variable in Swift?
Functionally, these are very similar, but I'd advise using the Model.shared
syntax because that makes it absolutely clear, wherever you use it, that you're dealing with a singleton, whereas if you just have that model
global floating out there, it's not clear what you're dealing with.
Also, with globals (esp with simple name like "model"), you risk of having some future class that has similarly named variables and accidentally reference the wrong one.
For a discussion about the general considerations regarding globals v singletons v other patterns, see Global Variables Are Bad which, despite the fairly leading title, presents a sober discussion, has some interesting links and presents alternatives.
By the way, for your "OCD friends" (within which I guess I must count myself, because I think it's best practice), not only would declare init
to be private
, but you'd probably declare the whole class to be final
, to avoid subclassing (at which point it becomes ambiguous to what shared
references).
Swift singleton vs. static properties/methods
To me, a simpler alternative would be to convert all properties and methods to static, and drop the sharedInstance property.
These do not do the same thing. The recommended approach is not actually a singleton at all. It's just a well-known instance. The concept of the Singleton pattern is that there must only be one instance. The concert of the shared instance pattern is that there can be more than one instance, but there is one that you probably want, and you would like easy access to it.
The advantage of shared instances is that they are not magical. They're just instances. That means that they can be handed around as values. They can be replaced with other instances that may be configured differently. They are easier to test (because they can be passed into functions).
True singletons are a very rigid pattern that should only be used when it is absolutely necessary that no other instance exist, usually because they interact with some external unique resource in a way that would create conflicts if there were multiples (this is pretty rare). Even in this case, in Swift, you should generally just make init
private to prevent additional instances being created.
If you look around Cocoa, you'll find that shared instances are extremely common for things that would be Singletons in other frameworks, and this has been very powerful. For instance, there is a well known NotificationCenter
called default
, and it's probably the only one you've ever used. But it's completely valid to create a private NotificationCenter
that's independent (I've actually done this in production code).
The fact that UIDevice.current
is how you access the device, rather than static methods, leaves open the possibility of new APIs that can handle multiple devices (it also helps with unit testing). In the earliest versions of iOS, the only UIScreen
was .main
, and it might have made sense to make it a singleton. But because Apple didn't, when mirroring was added in 4.3, it was simple to talk about the second screen (UIScreen.mirrored
). You should generally be very slow to assume that there can only be one of something.
Why not use a struct-based singleton in Swift
You can't use a struct fora singleton because struct
is a value type so when you assign it to a variable you get a copy. This can be easily shown
struct Singleton {
static var shared = Singleton()
var value: Int
private init() {
value = 0
}
}
Singleton.shared.value = 1
var otherSingleton = Singleton.shared
otherSingleton.value = 2
Now if we print the value
of both
print(Singleton.shared.value, otherSingleton.value)
we get
1 2
So otherSingleton
is clearly a separate instance so now we have 2 singletons :)
But if we simply change the type of Singleton
to class that is a reference type and then run the same code the result of the print is
2 2
since it is the same instance we have changed the value
property for.
Using Arrays in Singleton Class in iOS
Array in swift is implemented as Struct, which means Array is a value type and not a reference type. Value types in Swift uses copy on write (COW) mechanism to handle the changes to their values.
So in your ViewController
when you assigned
var PetArray = PetInfo.shared.petArray
your PetArray
was still pointing to the same array in your PetInfo.shared
instance (I mean same copy of array in memory) . But as soon as you modified the value of PetArray
by using
PetArray.append(pet)
COW kicks in and it creates a new copy of petArray
in memory and now your PetArray
variable in your ViewController and PetInfo.shared.petArray
are no longer pointing to same instances instead they are pointing to two different copies of array in memory.
So all the changes you did by using PetArray.append(pet)
is obviously not reflected when you access PetInfo.shared.petArray
in secondViewController
What can I do?
remove PetArray.append(pet)
and instead use PetInfo.shared.petArray.append(pet)
What are the other issues in my code?
Issue 1:
Never use Pascal casing for variable name var PetArray = PetInfo.shared.petArray
instead use camel casing var petArray = PetInfo.shared.petArray
Issue 2:
class PetInfo {
static let shared: PetInfo = PetInfo()
lazy var petArray: [PetInfo] = []
var PetID:Int
var PetName:String
...
init(){ .. }
}
This implementation will not ensure that there exists only one instance of PetInfo
exists in memory (I mean it cant ensure pure singleton pattern), though you provide access to instance of PetInfo
using a static variable named shared
there is nothing which stops user from creating multiple instances of PetInfo
simply by calling PetInfo()
as you did in let pet = PetInfo()
rather use private init(){ .. }
to prevent others from further creating instances of PetInfo
Issue 3:
You are holding an array of PetInfo
inside an instance of PetInfo
which is kind of messed up pattern, am not really sure as to what are you trying to accomplish here, if this is really what you wanna do, then probably you can ignore point two (creating private init) for now :)
Swift - Singleton without global access
You can use an atomic flag (for thread safety) to mark the singleton as being instantiated:
class Singleton {
static private var hasInstance = atomic_flag()
init() {
// use precondition() instead of assert() if you want the crashes to happen in Release builds too
assert(!atomic_flag_test_and_set(&type(of: self).hasInstance), "Singleton here, don't instantiate me more than once!!!")
}
deinit {
atomic_flag_clear(&type(of: self).hasInstance)
}
}
You mark the singleton as allocated in init
, and you reset the flag in deinit
. This allows you on one hand to have only one instance (if the original instance doesn't get deallocated), and on the other hand to have multiple instances, as long as they don't overlap.
App code: assuming that you'll keep a reference to the singleton, somewhere, that you inject downstream, then deinit
should never be called, which leads to only one possible allocation.
Unit testing code: if the unit tests properly do the cleanup (the tested singleton gets deallocated after every test), then there will be only one living instance at a certain point in time, which won't trigger the assertion failure.
Use self in singleton struct
A couple of thoughts:
A
struct
singleton is a contradiction in terms. A singleton is an object where there should be only one instance. Butstruct
is a value-type and has "copy" memory semantics. Consider:var a = StructA.shared
...The
a
is a copy of the so-called singleton, not a reference to it. To avoid this problem, the singleton should be aclass
, a reference type.I agree with Paulw11, that
self
is a simpler and more common approach. I'd also suggest, though, that by referencingself
, you can better write code that (a) is not dependent on the class being a singleton; and (b) opens the possibility of the class being subclassed at some future date.Given that I would advise
self
pattern, I would therefore also suggest avoiding obvious potential strong reference cycles (e.g. by employingweak
orunowned
references where needed). There's no point in knowingly creating what could be strong reference cycle simply because it happens to be a singleton. Why write code that you know you'd have to rewrite if you ever revisited the decision to use singleton pattern, especially when you know how easy it is to avoid strong references in the first place?FYI, I'm seeing the same behavior that you report, that if a
static
participates in a theoretical strong reference cycle, it's not identified as such. But if you set thatstatic
property tonil
(assuming it was variable and optional), the strong reference appears.This observation doesn't change my recommendation above, namely to avoid what you know would be a strong reference cycle in any other context. I'm merely confirming your empirical observation.
Regarding points 2 through 4 above (where I contemplate some potential eventual refactoring of singleton pattern into some other pattern), I should say that this is not a purely academic observation. It's not uncommon to have some singleton type, and later, as the project becomes more complicated or employs more unit tests, to revisit that decision and start employing dependency injection or other patterns. It would be a shame if you had to edit all of the individual functions as well. If you write the code to not depend upon the singleton nature of the object, you end up with more robust code base with fewer unnecessary internal dependencies.
Where Singleton object is allocated?
Swift allocates storage for
MyClass.shared
in the data segment, initialized to nil. The data segment's layout and initial contents are defined by the executable file. Historically, the heap started immediately at the end of the data segment, but on modern 64-bit systems with address space layout randomization (ASLR), I don't know if that's still true.Swift also allocates a
swift_once_t
in the data segment to record whetherMyClass.shared
has been initialized yet.Swift generates a getter function for
MyClass.shared
in the code segment. The getter function uses theswift_once_t
to initialize the storage ofMyClass.shared
the first time the getter is called. It looks approximately like this:var _storage_MyClass_shared: MyClass? = nil
var _once_MyClass_shared: swift_once_t = .init() // essentially, false
func _getter_MyClass_shared() -> MyClass {
swift_once(&_once_MyClass_shared, {
_storage_MyClass_shared = MyClass()
})
return _storage_MyClass_shared!
}The instance of
MyClass
is stored on the heap. It starts with a word containing theisa
pointer (to theMyClass
metadata), followed by a word containing (usually) reference counts, followed by storage for the object's instance variables. In your case, there are no instance variables, so there is no additional storage. The blue box labeledMyclass()
in your diagram, and the arrow pointing to it, do not exist.If
myClass
is at top-level (not inside a method or data type declaration), then it is also stored in the data segment along with anotherswift_once_t
that tracks whether it's been initialized, and Swift generates a getter for it in the code segment.If
myClass
is an instance variable of a data type, then it is stored as part of its containing object, which may be either on the stack or the heap (in the case of astruct
,enum
, or tuple) or always on the heap (in the case of aclass
oractor
).If
myClass
is a local variable in a function, then it is stored on the stack.
Related Topics
Fbsdkapplicationdelegate Application Openurl:Sourceapplication:Annotation Deprecated
How Are Optional Values Implemented in Swift
Can a Swift Property Wrapper Reference the Owner of the Property Its Wrapping
Include an Extension for a Class Only If iOS11 Is Available
How to Set a Specific Default Time for a Date Picker in Swift
Datepicker Using Time-Interval in Swiftui
Scenekit - Animation with Dae File Format
What's the Difference Between a View and a Viewcontroller
How to Reset/Restart Viewcontroller in Swift
Nsundomanager Casting Nsundomanagerproxy Crash in Swift Code
Swift: How to Access in Appdelegate Variable from the View Controller
Failed to Get Descriptors for Extensionbundleid
Simple Clickable Link in Cocoa and Swift
Access Properties via Subscripting in Swift
Nstextalignment.Justified for Uilabel Does Not Work