Do you need help on a specific subject? Use the contact form (Request a blog entry) on the right hand side.

2015-04-03

Swift "Singleton" Design Pattern

The singleton, probably the most famous design pattern in existence.
For the simple cases, Swift makes this an extremely easy one:

// The simpelest singleton possible

var simpleSingleton = SimpleSingleton()

class SimpleSingleton {
    private init() {}
}

And that is all. Anywhere in your project you can now use the singleton "simpleSingleton", no need to instantiate it, or retrieve a pointer to it. It's simply there when you need it.

Being a very simple singleton there are some drawbacks: you might get a clash with other variables. I.e. the name of the singleton must be unique in your software. For general purpose singletons there is a real chance that you will already have other variables (either local or global in scope) that have the same name.

We can remove the chance for name collisions by making the singleton private and providing an accessor function for it:

// Still simple, but avoids name collisions

private var simpleSingleton = SimpleSingleton()

class SimpleSingleton {
    
    class func singleton() -> SimpleSingleton {
        return simpleSingleton
    }
    
    private init() {}

}

Whenever you need the singleton, you obtain it first through the class function. You can find this pattern throughout Cocoa in things like: NSFileManager.defaultManager() or NSBundle.mainBundle()

The main drawback of these simple patterns is that the singleton is created during application startup. This can be annoying if it takes a lot of time or if the singleton is not always needed in every application run. And it becomes downright impossible if the singleton cannot be created during startup because not all of the data necessary is available at that time.

The solution is to delay the creation of the singleton to the time where its really needed (or can be created). The result looks like this

// Delay the creation of the singleton until it's really needed.

private var delayedSingleton: DelayedSingleton?

class DelayedSingleton {
    
    class func singleton() -> DelayedSingleton? {
        
        if delayedSingleton != nil {
            
            return delayedSingleton!
        
        } else {
        
            if let delayedSingleton = DelayedSingleton() {
                
                return delayedSingleton
                
            } else {
                
                // Oeps, the singleton could not be created,
                // take corrective action or inform the user
                // (or leave it to the callee)
                
                return nil
            }
        }
    }
    
    private init?() {}
}

In the above example, notice that the "init" function now returns an optional. This is done to cover the case where the creation of the singleton depends on data that may not be available. If it is possible to ensure that the singleton will be created, then remove the optional aspect of the returned value of both the init and the singleton accessor.

We are not done yet. The above solution works perfectly in a single thread, but in a multithreaded environment we risk the creation of multiple instances of the singleton, exactly what we did not want! In the above case, do note that even if multiple instances were created, the software would only keep the one created last. This could make for some very hard to find bugs!

Using Grand Central Dispatch it is easy to avoid the creation of multiple instances, here is the code:

// Multithreading safe singleton with delayed creation.

private var threadSafeDelayedSingleton: ThreadSafeDelayedSingleton?

class ThreadSafeDelayedSingleton {
    
    
    class func singleton() -> ThreadSafeDelayedSingleton {
        
        // Avoid dispatching if the singleton is already created!
        
        if threadSafeDelayedSingleton != nil { return threadSafeDelayedSingleton! }
        
        
        // Create the singleton under explicit synchronisation
        
        let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
        
        dispatch_sync(queue, { self.createSingleton() } )
        
        return threadSafeDelayedSingleton!
    }
    
    
    private class func createSingleton() {
        
        // In the unlikely case that more than one creation request is scheduled,
        // only create the singleton the first time.
        
        if threadSafeDelayedSingleton == nil {
            threadSafeDelayedSingleton = ThreadSafeDelayedSingleton()
        }
    }
    
    private init() {}
}

Notice that I removed the optional from the init and the singleton accessor. In a multithreaded environment you better be sure that the creation of the singleton succeeds! If not, then a whole new can of worms opens which is beyond the scope of this article.

Btw: The very first example of a singleton creation is also multithreading safe ;-)

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

No comments:

Post a Comment