Singletons in Objective-C

January 30, 2012

I've done a fair amount of programming in Objective-C for my iOS applications and occasionally a Singleton instance of a class is a convenient pattern.

I am fully aware of the controversy revolving around Singletons and their use. I personally subscribe to the tenet that if a particular programming construct is useful, and not completely corrupting of the overall design of the application, I might as well take advantage of it. Singletons, in my opinion fall into this category.

This article will illustrate the technique I use to create Singletons in Objective-C.

The usual implementation of a Singleton in most programming languages involves 3 things:

  1. creation of a non-exposed1 static variable that will contain a pointer to the one-and-only-one instance of the class
  2. creation of a static function on the class that will retrieve this instance, and create the one-and-only-one instance if it has not yet been created
  3. creation of a non-exposed constructor for the class

In Objective-C, items 1) and 2) are are very straight forward, well documented and easy to implement. Item 3), on the other hand, is not so obvious.

There are many reasons for this, but it basically comes down to a few core concepts in Objective-C that are different than most other languages. Namely, Objective-C uses a two-step memory allocation/initialization process, it uses reference counting to manage memory, and all objects inherit from a main superclass, that exposes a default constructor.

To overcome this, a good number of functions need to be overridden to ensure that there is only one instance of the class ever created, and that the reference counting system doesn't reclaim that instance.

In this tutorial, we will be creating an Objective-C class called MySingleton.

Lets start with the easy stuff first. Let's create the source and header files for out class:

MySingleton.h

#import <Foundation/Foundation.h>

@interface MySingleton : NSObject {
}
@end

MySingleton.m

#import "MySingleton.h"

@implementation MySingleton
@end

1) Create a non-exposed static variable that will contain the single instance of the class.

To do this, is simply a matter of declaring a static variable in the source file of the class in which we want to make a Singleton.

In MySingleton.m add the following:

    #import "MySingleton.h"

    static MySingleton *s_instance = nil;

    @implementation
    @end

2) Create a static function on the class that will retrieve this instance

This is the standard Objective-C static function declaration of a function on a class (i.e. use the '+' to denote a static class function instead of the '-').

In MySingleton.h add the following:

    #import <Foundation/Foundation.h>

    @interface MySingleton : NSObject {
    }
    + (MySingleton*) instance;
    @end

In MySingleton.m add the following:

    #import "MySingleton.h"

    @implementation

    + (MySingleton *) instance {
        @synchronized(self) {
            if (s_instance == nil) {
                // Don't assign to s_instance here
                [[self alloc] init];
            }
        }
        return s_instance;
    }

    @end

The instance function above, looks very similar to what you might be used to seeing in other language implementations of the Singleton pattern. Essentially, it checks to see if the one-and-only-one instance has already been created, and if not creates it.

The point in the above function that may not be completely obvious is that the allocation and initialization is not assigned to the static instance variable. This will be handled by the override of the alloc function, because, essentially, it is the allocation of the memory for a new instance that we are trying to prevent. This will be evident in the next section.

3) Create a non-exposed constructor for the class

As stated above, there really is no way of creating a non-exposed constructor in an Objective-C class. Rather, we will override certain functions to emulate this behavior.

In MySingleton.m add the following:

    #import "MySingleton.h"

    @implementation

    + (MySingleton *) instance {
        @synchronized(self) {
            if (instance == nil) {
                // Don't assign to s_instance here
                [[self alloc] init];
            }
        }
        return instance;
    }

    + (id) allocWithZone:(NSZone *)zone {
        @synchronized(self) {
            if (s_instance == nil) {
                // Assignment and return on first allocation
                s_instance = [super allocWithZone:zone];
                return s_instance;
            }
        }
        // On subsequent attempts to allocate a new instance, 
        // return nil
        return nil;
    }

    - (id) copyWithZone:(NSZone *)zone {
        // Since this is a Singleton, we certainly don't
        // want to copy this object, so just return ourselves
        return self;
    }

    - (id) retain {
        // We don't need to do anything here
        return self;
    }

    - (unsigned)retainCount {
        // Indicates an object that cannot be released
        return UINT_MAX;
    }

    - (oneway void) release {
        // We want to make sure any release messages are 
        // ignored, so don't do anything here
    }

    - (id) autorelease {
        // Again, any release requests should be ignored.
        return self;
    }

    @end

That's a fair bit of code, so let's take it function by function.

+ (id) allocWithZone:(NSZone *)zone

This function by default will allocate the memory needed for an instance of the class. Since we only want one of instance of this class, we check to see if we have already allocated one. If not, we allocate one, and return it to the caller. Notice here is where we assign this newly allocated memory to our static variable.

Most developers are not used to seeing the allocWithZone call, rather they typically use the simpler alloc function. Be assured, the the alloc call delegates to this function with the default zone.

- (id) copyWithZone:(NSZone *)zone

This function overrides the copy function to simply return the instance. Again, the simpler copy method delegates to this one with the default zone.

- (id) retain

This function simply needs to return a reference to the instance. Retain counts, have no meaning with a singleton, since there should only be one instance that lasts the entire time the process is running.

When using a Singleton, there should be no need to ever call retain/release on the instance, but this is added as a protection mechanism. If a developer accidentally tries to retain the singleton no harm will come of it (other than a few wasted cycles).

- (unsigned)retainCount

This function returns the maximum value of an integer as its retain count. This effectively says that this object should never be released.

- (oneway void) release

The release function typically decrements the retain count of the object and cleans up the memory if there are no more references to it. Since we don't want to do this, ever, this function is basically a no-op.

Again, as stated above, there should be no need to ever call retain/release on the instance in the case of a Singleton, and this is added for protection.

- (id) autorelease

The autorelease function adds the object to the most recently invoked auto-release pool for releasing the next time the pool is drained. We simply return a reference to the instance and don't do any auto-release processing.

Initialization

At this point all of the pieces to may the class a singleton are there. If you have custom initialization code that needs to be run when the Singleton is first created, you can create an init function, just like you would normally, and add the logic there.

Usage

Now to use the Singleton you simply have to:

[[MySingleton instance] <some_function_here>];

That's all there is to it. You can download the source files for this example here MySingleton.zip.

[1] non-exposed means not exposed via the API to users of the class, in the typical parlance this means either protected or private, or in a source file with no corresponding externalized reference in a header file.