Open In App

Singleton Class in Objective-C

Objective-C serves as a programming language widely employed to construct applications intended for both macOS and iOS. Among the many design patterns that find extensive usage in Objective-C, the Singleton pattern proves quite noteworthy. A Singleton represents a class in that instantiation remains restricted to a single instance throughout the entire application. In this discourse, we delve into the Singleton pattern and its implementation in Objective-C.

What is a Singleton Class?

The Singleton paradigm denotes a class instantiation restricted to a singular occurrence across the entire application. It establishes a global access point allowing all objects to connect with it. Such a pattern is preferred for shared resources, enabling a sole entry point, thus minimizing resource creation redundancies.



Benefits of Singleton Class

Implementing Singleton Class in Objective-C

There are two approaches to implementing a Singleton class in Objective-C:

Approach 1: Using a Static Instance Variable

This method involves using a static instance variable to retain the sole instance of the class, with the variable initialized to nil when the class is first loaded. Upon the calling of the shared instance method, an assessment is conducted to determine if the instance variable is nil. Should it be found empty, a new class instance is generated, and assigned to the variable. On the other hand, if it is non-nil, the current class instance is returned.



Below is the Objective-C program to implement a singleton class using static instance variables.




// Objective-C program to implement
// static instance variables
 
// Importing the Foundation framework
// which includes NSObject and other classes
#import <Foundation/Foundation.h>
 
// Declaring the interface for our singleton class
// and conforming to the NSCopying protocol
@interface MyClass : NSObject <NSCopying>
 
// Declaring the sharedInstance method which
// returns a MyClass instance
+ (MyClass *)sharedInstance;
 
@end
 
// Defining the implementation for our
// singleton class
@implementation MyClass
 
// Declaring the static variable which will hold
// the shared instance of MyClass
static MyClass *sharedInstance = nil;
 
// Implementing the sharedInstance method
+ (MyClass *)sharedInstance
{
  // Checking if sharedInstance is nil (i.e.
  // the shared instance hasn't been created yet)
  if (sharedInstance == nil)
  {
    // Creating a new instance of MyClass using
    // the superclass's allocWithZone method
    sharedInstance = [[super allocWithZone:NULL] init];
  }
   
  // Returning the shared instance
  return sharedInstance;
}
 
// Overriding the allocWithZone method to always
// return the shared instance
+ (id)allocWithZone:(NSZone *)zone
{
  return [self sharedInstance];
}
 
// Overriding the copyWithZone method to always
// return the same instance (since this is a singleton)
- (id)copyWithZone:(NSZone *)zone
{
  return self;
}
 
// Implementing the init method to initialize any
// instance variables (if needed)
- (id)init
{
  self = [super init];
  if (self != nil)
  {
    // Initialize instance variables here
  }
  return self;
}
 
@end
 
// Main function for testing our singleton
int main(int argc, const char * argv[])
{
  // Creating two instances of MyClass using
  // the sharedInstance method
  MyClass *myObject1 = [MyClass sharedInstance];
  MyClass *myObject2 = [MyClass sharedInstance];
   
  // Checking if both instances are the same (i.e.
  // the sharedInstance method is working correctly)
  if (myObject1 == myObject2)
  {
    NSLog(@"Both objects are the same instance");
  }
  else
  {
    NSLog(@"Objects are different instances");
  }
   
  // Returning 0 to indicate successful completion
  // of the program
  return 0;
}

Output:

 

Explanation:

Approach 2: Using dispatch_once

To guarantee the thread safety of the Singleton class initialization process, the dispatch_once function is utilized in this method. The function is invoked once throughout the entire lifetime of the application, ensuring synchronization and safety. Upon the calling of the sharedinstance method, the dispatch_once function comes into play, generating the sole instance of the class.

Below is the Objective-C program to implement a singleton class using a dispatch_once:




// Objective-C program to implement a
// singleton class using a dispatch_once
#import <Foundation/Foundation.h>
 
// Declare an interface for the singleton class
@interface MyClass : NSObject
 
// Declare a class method to get the shared
// instance of the class
+ (MyClass *)sharedInstance;
 
@end
 
// Implement the interface for the singleton class
@implementation MyClass
 
// Create a static variable to hold the shared
// instance of the class
static MyClass *sharedInstance = nil;
 
// Define the class method to get the shared
// instance of the class
+ (MyClass *)sharedInstance
{
  // If the shared instance is nil, create it
  if (sharedInstance == nil)
  {
    sharedInstance = [[super allocWithZone:NULL] init];
  }
     
  // Return the shared instance
  return sharedInstance;
}
 
// Override the allocWithZone method to always
// return the shared instance
+ (id)allocWithZone:(NSZone *)zone
{
  return [self sharedInstance];
}
 
// Override the copyWithZone method to always
// return the existing instance
- (id)copyWithZone:(NSZone *)zone
{
  return self;
}
 
// Override the init method to always return
// the shared instance
- (id)init
{
  return sharedInstance;
}
 
@end
 
// Define the main function
int main(int argc, const char * argv[])
{
  // Create two instances of the singleton class
  MyClass *instance1 = [MyClass sharedInstance];
  MyClass *instance2 = [MyClass sharedInstance];
   
  // Check if both instances are the same
  if (instance1 == instance2)
  {
    NSLog(@"Both objects are the same instance");
  }
  else
  {
    NSLog(@"Objects are different instances");
  }
   
  return 0;
}

Output:

Explanation:

Conclusion

Both approaches have their advantages and disadvantages. The first approach is simpler to implement, but it is not thread-safe. The second approach is thread-safe, but it is more complex to implement. It is important to choose the right approach based on the specific requirements of the application.


Article Tags :