Chapter 7. Singleton

In mathematics and logic, a singleton is defined as "a set that contains exactly one element." So no matter how big a pouch is, every time you are trying to get a marble out of it you will get only the same one. Under what situations do we need a singleton in software? Think about resources that can be shared only in a system and no copies of them can be made available to others. For example, the GPS device is the sole hardware on the iPhone that provides coordinates of the device in real time. The CLLocationManager class of the CoreLocation framework provides a single access point to any services that the GPS device offers. Some people may think, if I can make a copy of CLLocationManager, then can I get an extra set of GPS services to my application? That sounds like a fantasy—you create two copies of software GPS for the price of one hardware GPS. But in reality, you still get only one GPS at a single time because there is only one GPS in the device that makes actual connections with satellites in the sky. So if you think you wrote a killer app that can manipulate two separate GPS connections at a time and want to brag about it to your friends, think twice!

A singleton class in an object-oriented application always returns the same instance of itself. It provides a global access point for the resources provided by the object of the class. A design pattern that is related to these kinds of designs is called the Singleton pattern.

In this chapter, we will be exploring some possibilities for implementing and using the Singleton pattern in Objective-C and the Cocoa Touch framework for the iOS.

What Is the Singleton Pattern?

The Singleton pattern is almost the simplest form of design pattern ever. The intent of the pattern is to make an object of a class be the sole instance in a system. To achieve that, we can begin with the point where clients instantiate it. So we need to "block" every access to create the object with a mechanism that allows only a single instance of the object's class to go out. We can use a factory method (Chapter 4) to bottleneck the instantiation process. That method should be a static method (class method), as it wouldn't make sense to allow an instance of the class to create another sole instance. Figure 7-1 shows a class structure of a simple singleton.

A static structure of the Singleton pattern

Figure 7-1. A static structure of the Singleton pattern

The static uniqueInstance is the only instance of Singleton as a class variable with which static sharedInstance will return it to clients. Usually, sharedInstance would check and see if uniqueInstance is instantiated. If not, it will create one before returning the uniqueInstance.

Note

THE SINGLETON PATTERN: Ensures a class has only one instance, and provide a global point of access to it.[5]

When Would You Use the Singleton Pattern?

You'd think about using the Singleton pattern when

  • There must be exactly one instance of a class with which it must be accessible from a well-known access point, e.g., a factory method (Chapter 4).

  • The sole instance can be extended only by subclassing, and it won't break client code with the extended object.

The Singleton pattern provides a well-known access point to client classes that want to create a unique instance of and access to a shared resource. Although a static global object reference or a class method can provide a global access point, the global object cannot prevent the class getting instantiated more than once, and the class method lacks the flexibility of decoupling.

A static global variable holds a single reference to an instance of a class. Another class or a method that can access that global variable is, in fact, sharing the same copy with other classes or methods that use the same variable. That sounds like what we are after in this chapter. Everything seems fine if we use only the same global variable throughout the whole application. So, in fact, we don't need the Singleton pattern. But hey, wait a minute; what if there is somebody in your team or a consultant who has defined the same type of static global variable as yours? Then there will be two copies of the same global object type living in the same application—so a global variable doesn't really solve the problem.

A class method provides shared services without creating an object of it. A single instance of the resource is maintained within the class method. However, this approach lacks the flexibility if the class needs to be subclassed to provide better services.

A singleton class can guarantee a single, consistent, and well-known access point to create and access a single object of the class. The pattern provides the flexibility such that any of its subclasses can override the instance method and have total control over object creation of itself without changing code in the client. Or even better, the instance implementation in the parent class can handle dynamic object creation. The actual type of a class can be determined to make sure the correct object is created at runtime. This technique will be discussed later in the chapter.

There is a flexible version of the Singleton pattern in which a factory method always returns the same instance, but you can also allocate and initialize additional instances. This less "strict" version of the pattern is discussed in "Using the NSFileManager Class" later in this chapter.

Implementing a Singleton in Objective-C

There are some things that we need to think about in order to design a singleton class properly. The first question we need to ask is, how can we make sure there is only one instance of a class that can be created? Clients in an application written in other object-oriented programming languages, like C++ and Java, cannot create an object of a class if its constructor is declared private. Then what about Objective-C?

Every Objective-C method is public and the language itself is dynamically typed, so every class can send each other's messages (method calling in C++ and Java) without much compile time checking (only compiler warnings if a message in question is not declared). Also the Cocoa (including Cocoa Touch) framework utilizes reference counting memory management to maintain objects' life span in memory. All of these features make implementing a singleton in Objective-C quite challenging.

In the original example of the Design Patterns book, the C++ example of the Singleton pattern is as shown in Listing 7-1.

Example 7-1. The Original C++ Singleton Class Appearing in Design Patterns

class Singleton
{
public:
  static Singleton *Instance();

protected:
  Singleton();

private:
  static Singleton *_instance;
};
Singleton *Singleton::_instance = 0;

Singleton *Singleton::Instance ()
{
  if (_instance == 0)
  {
    instance = new Singleton;
  }

  return _instance;
}

As it is described in the book, the implementation in C++ is simple and straightforward. In the static Instance() method, the static _instance instance variable is checked to see if it is 0 (NULL). If so, a new Singleton object will be created and then an instance of it will be returned. Some of you may think an Objective-C version shouldn't be much different from its brother's version and should look like Listings 7-2 and 7-3.

Example 7-2. A Class Declaration of Singleton in Singleton.h

@interface Singleton : NSObject
{

}

+ (Singleton *) sharedInstance;

@end

Example 7-3. The Implementation of the sharedInstance Method in Singleton.m

@implementation Singleton

static Singleton *sharedSingleton_ = nil;

+ (Singleton *) sharedInstance
{
  if (sharedSingleton_ == nil)
  {
    sharedSingleton_ = [[Singleton alloc] init];
  }
  return sharedSingleton_;
}

@end

If that's the case, then this is a very easy chapter to write and read, and you've already learned one pattern implemented in Objective-C. In reality, there are some hurdles that we need to get over to make the implementation robust enough to live in a real application. If you need to implement a "strict" version of the Singleton pattern, then there are two major hurdles that you need to address so it can be used in the real world:

  • A calling object cannot instantiate a singleton object through other means of allocation. Otherwise, multiple instances of the singleton class could be created.

  • Restrictions on instantiating a singleton object should also coexist with the reference counting memory models.

Listing 7-4 shows an implementation that should be close to the one that we are looking at. The code is quite long, so we will break it up into a few parts to discuss.

Example 7-4. A More Appropriate Implementation of Singleton in Objective-C

#import "Singleton.h"

@implementation Singleton

static Singleton * sharedSingleton = nil;

+ (Singleton*) sharedInstance
{
  if (sharedSingleton_ == nil)
  {
    sharedSingleton_ = [[super allocWithZone:NULL] init];
  }

  return sharedSingleton_;
}

Inside the sharedInstance method, just like in the first example, it first checks if the sole instance of the class is created, otherwise it creates a new one and returns it. But this time, it calls [[super allocWithZone:NULL] init] to create a new instance instead of using other methods such as alloc. Why super but not self? It's because we have overridden the basic object allocation methods in self, so we need to "borrow" the functionality from its parent, in this case NSObject, to help do the low-level memory allocation chores for us.

+ (id) allocWithZone:(NSZone *)zone
{
  return [[self sharedInstance] retain];
}

- (id) copyWithZone:(NSZone*)zone
{
  return self;
}

There are a few methods that are related to the memory management, which we need to take care of in the Singleton class. In allocWithZone:(NSZone *)zone, the method returns only the class instance that is returned from the sharedInstance method. In the Cocoa Touch framework, by calling a class's allocWithZone:(NSZone *)zone method, memory of an instance will be allocated, its reference count will be set to 1, and the instance will be returned. We have seen that the alloc method is used in many situations; in fact, alloc calls allocWithZone: with the zone set to NULL to allocate memory for the new instance in the default zone. The details of object creation and memory management are outside the scope of this book. You can consult the documentation for any further details.

Likewise, we also need to override the copyWithZone:(NSZone*)zone method to make sure it won't return a copy of the instance but the same one by returning self.

- (id) retain
{
  return self;
}

- (NSUInteger) retainCount
{
  return NSUIntegerMax;  // denotes an object that cannot be released
}

- (void) release
{
  // do nothing
}

- (id) autorelease
{
  return self;
}

@end

Other methods, like retain, release, and autorelease, are overridden to make sure they won't do anything (in a reference counting memory model) except just to return self. retainCount returns NSUIntegerMax (4,294,967,295) to keep the instance alive as long as the application lives.

We have pretty much covered what a singleton in Objective-C should look like. However, there is still something else that we need to think about carefully before we can use it. What if we want to subclass the original Singleton? We will answer that question now.

Subclassing a Singleton

The alloc call is forwarded to super, which means NSObject will take care of the object allocation. If we subclass Singleton without any modification, the returned instance is always Singleton. Because Singleton overrides all the instantiation-related methods, it is quite tricky to subclass it. But we are quite lucky; we can use some Foundation functions to instantiate whatever object based on its class type. One of them is id NSAllocateObject (Class aClass, NSUInteger extraBytes, NSZone *zone). So if we want to instantiate an object of a class called "Singleton," we can do the following:

Singleton *singleton = [NSAllocateObject ([Singleton class], 0, NULL) init];

The first parameter is the type of the Singleton class. The second parameter is for any extra bytes for indexed instance variables, which is always 0. The third parameter is to specify an allocated zone in memory; it is NULL for a default zone most of the time. So you can instantiate whatever objects with this function by providing their class types. What does it have to do with subclassing Singleton? Let's recall that the original sharedInstance method looks like this:

+ (Singleton*) sharedInstance
{
  if (sharedSingleton_ == nil)
  {
    sharedSingleton_ = [[super allocWithZone:NULL] init];
  }

  return sharedSingleton_;
}

If we use the trick of NSAllocateObject to create an instance, then it will become like this:

+ (Singleton *) sharedInstance
{
 if (sharedSingleton_ == nil)
 {
   sharedSingleton_ = [NSAllocateObject([self class], 0, NULL) init];
 }

  return sharedSingleton_;
}

So now no matter whether we are instantiating Singleton or any of its subclasses, that version should take care of that nicely.

Thread Safety

If a singleton object is intended to be accessed by multiple threads, then it is crucial to make it thread-safe. The Singleton class in the example is good only for general use. To make it thread-safe, we can put some @synchronized() blocks or NSLock instances around the nil check for the sharedSingleton_ static instance. If there are some other properties that also need to be protected, we can make them atomic as well.

Using Singletons in the Cocoa Touch Framework

When you wade through the Cocoa Touch Developer's documentation, you will find numerous singleton classes in the framework here and there. We will talk about a few of them in this section, UIApplication, UIAccelerometer, and NSFileManager.

Using the UIApplication Class

One of the most commonly used singleton classes in the framework is the UIApplication class. It provides a centralized point of control and coordination for iOS applications.

Every app has exactly one instance of UIApplication. It's created as a singleton object by the UIApplicationMain function when an app is launched. Thereafter, the same instance of the UIApplication can be accessed through its sharedApplication class method.

A UIApplication object does many housekeeping tasks for an application, including initial routing of incoming user events as well as dispatching action messages for UIControl objects to appropriate target objects. It also maintains a list of all the UIWindow objects being opened in the app. The application object is always assigned with a UIApplicationDelegate object, to which the application informs any significant runtime events, such as application did launch, low-memory warnings, application termination, and background process executions in an iOS application. They give an opportunity to the delegate to respond appropriately.

Using the UIAccelerometer Class

Another common singleton in the Cocoa Touch framework is the UIAccelerometer. The UIAccelerometer class allows an application to sign up to receive acceleration-related data from the built-in accelerometer in an iOS device. An application can use reported linear acceleration changes along the primary axes in three-dimensional space to detect both the current orientation of the device and any instantaneous changes to that orientation.

The UIAccelerometer is a singleton, so you cannot create its objects directly. Instead, its sharedAccelerometer singleton class method should be called to access its sole instance. Then you can set its updateInterval as well its delegate properties with your own delegate object to receive any reported acceleration data from the singleton instance.

Using the NSFileManager Class

The NSFileManager used to be a "strict" implementation of the Singleton pattern prior Mac OS X v 10.5 and in iOS 2.0. An invocation to its init method is a no-op (does nothing), and its only instance can be created and accessed only through the defaultManager class method. However, its singleton implementation is not thread-safe. Now creating new instances of NSFileManager is recommended for thread-safety. This approach is considered a more flexible singleton implementation in which a factory method always returns the same instance, but you can allocate and initialize additional instances as well.

If you need to implement a "strict" singleton, you need to have an implementation similar to the example described in the previous sections. Otherwise, do not override allocWithZone: and the other methods following it.

Summary

The Singleton pattern is one of the most commonly used patterns in almost any type of application, not just for iOS application development.

As long as an application requires a centralized class that coordinates the services of its own, the class should generate a singleton instance rather than multiple instances.

This chapter marks the end of this part about object creation. In the next part, we will see some design patterns that focus on adapting/consolidating objects with different interfaces.



[5] The original definition appeared in Design Patterns, by the "Gang of Four" (Addison-Wesley, 1994).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.97.75