178

How do I convert (or create) a singleton class that compiles and behaves correctly when using automatic reference counting (ARC) in Xcode 4.2?

Brad Larson
  • 170,088
  • 45
  • 397
  • 571
cescofry
  • 3,706
  • 3
  • 26
  • 22
  • 1
    I recently found an Article from Matt Galloway going quite in depth on Singletons for both ARC and manual memory management environments. http://www.galloway.me.uk/tutorials/singleton-classes/ – cescofry Oct 14 '13 at 11:44

10 Answers10

402

In exactly the same way that you (should) have been doing it already:

+ (instancetype)sharedInstance
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}
catlan
  • 25,100
  • 8
  • 67
  • 78
Nick Forge
  • 21,344
  • 7
  • 55
  • 78
  • 9
    You just don't do any of the memory management hokey pokey Apple used to recommend in http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32 – Christopher Pickslay Sep 27 '11 at 21:05
  • what is onceToken? i understand the need for a token but what is it? an enum? can i type what ever... – Yogev Shelly Jun 25 '12 at 16:50
  • @YogevShelly `dispatch_once_t` is defined in `dispatch/once.h`, and it's typedefed as a `long`. It's probably used as an index on an array or some other global data structure which stores the state of every Dispatch Once token. You can call it whatever you want, and it will still work. – Nick Forge Jun 26 '12 at 03:44
  • I'm working from "Pro Objective C Design Patterns" from Apress. The author demonstrates a pre-ARC example of the Singleton pattern. This one works fine for but can someone share the documentation on this pattern please? – aaronium112 Jul 16 '12 at 19:52
  • 1
    @MakingScienceFictionFact, you might want to take a look at [this post](http://lukeredpath.co.uk/blog/a-note-on-objective-c-singletons.html) – kervich Jul 24 '12 at 06:58
  • Why isn't `static MyClass *sharedInstance = nil;` be placed outside of the method? – David Aug 02 '12 at 22:34
  • 1
    @David Because the whole idea is that access to `sharedInstance` is safely encapsulated within a method that ensures it will be created properly. Nothing outside of that method should have access to it directly. If you needed to explicitly clear or nil-out the `sharedInstance` or something, then you would need to place it outside of the method. – Nick Forge Aug 03 '12 at 01:30
  • @NickForge but if you were to call shared instance multiple times, wouldn't the shared instance be two different objects? – David Aug 03 '12 at 02:43
  • 6
    @David `static` variables declared within a method/function are the same as a `static` variable declared outside a method/function, they are just only valid within the scope of that method/function. Every separate run through the `+sharedInstance` method (even on different threads) will 'see' the same `sharedInstance` variable. – Nick Forge Aug 03 '12 at 04:08
  • @NickForge I did some testing and it is what you say. Thank you. – David Aug 03 '12 at 05:26
  • 9
    What about if somebody calls [[MyClass alloc] init]? That would create a new object. How can we avoid this (other than declaring static MyClass *sharedInstance = nil outside the method). – Ricardo Sanchez-Saez Dec 07 '12 at 10:22
  • Because `static` variables are `0` (or `nil`) by default, the `= nil` is redundant. – meaning-matters May 20 '13 at 10:45
  • 1
    @RicardoSánchez-Sáez You can delete `+alloc~` method to prevent external instantiation completely. I posted an example as an answer. – eonil Oct 16 '13 at 09:47
  • 1
    Personally, I think *actively preventing* more than one instantiation is usually an incorrect course of action. Provide a shared instance accessor if appropriate, but hard-wiring only one instance is usually wrong IMO. Objective C is a dynamic language with a non-nanny-state culture. YMMV depending on if the singleton class is internal to a project vs. available over a public API. – occulus Nov 04 '13 at 15:44
  • 2
    If another programmer messes up and calls init when they should have called sharedInstance or similar, it's their error. Subverting the fundamentals, and basic contracts, of the language in order to stop others potentially making mistakes seems quite wrong. There's more discussion at http://boredzo.org/blog/archives/2009-06-17/doing-it-wrong – occulus Nov 04 '13 at 16:00
8

if you want to create other instance as needed.do this:

+ (MyClass *)sharedInstance
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[MyClass alloc] init];
        // Do any other initialisation stuff here
    });
    return sharedInstance;
}

else,you should do this:

+ (id)allocWithZone:(NSZone *)zone
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [super allocWithZone:zone];
    });
    return sharedInstance;
}
DongXu
  • 1,525
  • 14
  • 12
  • 1
    True/False: The `dispatch_once()` bit means that you won't get additional instances, even in the first example...? – Olie May 09 '13 at 18:04
  • 4
    @Olie: False, because client code can do `[[MyClass alloc] init]` and bypass the `sharedInstance` access. DongXu, you should look at [Peter Hosey's Singleton article](http://boredzo.org/blog/archives/2009-06-17/doing-it-wrong). If you're going to override `allocWithZone:` to prevent more instances from being created, you also should override `init` to prevent the shared instance from being re-initialized. – jscs May 20 '13 at 19:43
  • Ok, that's what I thought, hence the `allocWithZone:` version. Thx. – Olie May 20 '13 at 23:35
  • 2
    This completely breaks the contract of allocWithZone. – occulus Nov 04 '13 at 15:53
  • For more discussion on "to Singleton or not", see http://boredzo.org/blog/archives/2009-06-17/doing-it-wrong – occulus Nov 04 '13 at 15:59
  • 1
    singleton just means "only one object in memory at any time",this is one thing,being re-initialized is another thing. – DongXu Nov 05 '13 at 03:50
  • To make this work with `allocWithZone:`, you should keep a dictionary of `MyClass` objects, where the keys are the `NSZone`s. – Ky - Feb 05 '16 at 19:44
5

This is a version for ARC and non-ARC

How To use:

MySingletonClass.h

@interface MySingletonClass : NSObject

+(MySingletonClass *)sharedInstance;

@end

MySingletonClass.m

#import "MySingletonClass.h"
#import "SynthesizeSingleton.h"
@implementation MySingletonClass
SYNTHESIZE_SINGLETON_FOR_CLASS(MySingletonClass)
@end
Igor
  • 4,235
  • 3
  • 34
  • 32
2

This is my pattern under ARC. Satisfies new pattern using GCD and also satisfies Apple's old instantiation prevention pattern.

@implementation AAA
+ (id)alloc
{
    return  [self allocWithZone:nil];
}
+ (id)allocWithZone:(NSZone *)zone
{
    [self doesNotRecognizeSelector:_cmd];
    abort();
}
+ (instancetype)theController
{
    static AAA* c1  =   nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^
    {
        c1  =   [[super allocWithZone:nil] init];

        // For confirm...       
        NSLog(@"%@", NSStringFromClass([c1 class]));    //  Prints AAA
        NSLog(@"%@", @([c1 class] == self));            //  Prints 1

        Class   real_superclass_obj =   class_getSuperclass(self);
        NSLog(@"%@", @(real_superclass_obj == self));   //  Prints 0
    });

    return  c1;
}
@end
eonil
  • 83,476
  • 81
  • 317
  • 516
  • 1
    Won't this result in `c1` being an instance of `AAA`'s superclass? You need to call `+alloc` on `self`, not on `super`. – Nick Forge Oct 17 '13 at 00:44
  • @NickForge `super` doesn't mean the super-class object. You cannot get super-class object It just means routing messages to the super-class version of method. `super` still points `self` class. If you want to get super-class object, you need to get runtime reflection functions. – eonil Oct 17 '13 at 03:55
  • @NickForge And `-allocWithZone:` method is just a simple chain to runtime's allocation function to offer overriding point. So ultimately, `self` pointer == current class object will be passed to allocator, and finally `AAA` instance will be allocated. – eonil Oct 17 '13 at 04:02
  • you're correct, I'd forgotten the subtleties of how `super` works in class methods. – Nick Forge Oct 17 '13 at 05:19
  • Remember to use #import – Ryan Heitner Sep 10 '14 at 05:23
2

Read this answer and then go and read the other answer.

You must first know what does a Singleton mean and what are its requirements, if you don't understand it, than you won't understand the solution--at all!

To create a Singleton successfully you must be able to do the following 3:

  • If there was a race condition, then we must not allow multiple instances of your SharedInstance to be created at the same time!
  • Remember and keep the value among multiple invocations.
  • Create it only once. By controlling the entry point.

dispatch_once_t helps you to solve a race condition by only allowing its block to be dispatched once.

Static helps you to “remember” its value across any number of invocations. How does it remember? It doesn't allow any new instance with that exact name of your sharedInstance to be created again it just works with the one that was created originally.

Not using calling alloc init (i.e. we still have alloc init methods since we are an NSObject subclass, though we should NOT use them) on our sharedInstance class, we achieve this by using +(instancetype)sharedInstance, which is bounded to only be initiated once, regardless of multiple attempts from different threads at the same time and remember its value.

Some of the most common system Singletons that come with Cocoa itself are:

  • [UIApplication sharedApplication]
  • [NSUserDefaults standardUserDefaults]
  • [NSFileManager defaultManager]
  • [NSBundle mainBundle]
  • [NSOperations mainQueue]
  • [NSNotificationCenter defaultCenter]

Basically anything that would need to have centralized effect would need to follow some sort of a Singleton design pattern.

Community
  • 1
  • 1
mfaani
  • 33,269
  • 19
  • 164
  • 293
1

Alternatively, Objective-C provides the +(void)initialize method for NSObject and all its sub-classes. It is always called before any methods of the class.

I set a breakpoint in one once in iOS 6 and dispatch_once appeared in the stack frames.

Walt Sellers
  • 3,806
  • 30
  • 35
0

Singleton Class : No one can create more than one object of class in any case or through any way.

+ (instancetype)sharedInstance
{
    static ClassName *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[ClassName alloc] init];
        // Perform other initialisation...
    });
    return sharedInstance;
}
//    You need need to override init method as well, because developer can call [[MyClass alloc]init] method also. that time also we have to return sharedInstance only. 

-(MyClass)init
{
   return [ClassName sharedInstance];
}
Yogi
  • 253
  • 3
  • 5
  • 1
    If someone calls init, init will call sharedInstance, sharedInstance will call init, init will call sharedInstance a second time, then crash! First, this is an infinite recursion loop. Second, the second iteration of calling dispatch_once will crash because it can't be called again from inside dispatch_once. – Chuck Krutsinger Jul 14 '18 at 00:27
0

There are two issues with the accepted answer, which may or may not be relevant for your purpose.

  1. If from the init method, somehow the sharedInstance method is called again (e.g. because other objects are constructed from there which use the singleton) it will cause a stack overflow.
  2. For class hierarchies there is only one singleton (namely: the first class in the hierarchy on which the sharedInstance method was called), instead of one singleton per concrete class in the hierarchy.

The following code takes care of both of these problems:

+ (instancetype)sharedInstance {
    static id mutex = nil;
    static NSMutableDictionary *instances = nil;

    //Initialize the mutex and instances dictionary in a thread safe manner
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mutex = [NSObject new];
        instances = [NSMutableDictionary new];
    });

    id instance = nil;

    //Now synchronize on the mutex
    //Note: do not synchronize on self, since self may differ depending on which class this method is called on
    @synchronized(mutex) {
        id <NSCopying> key = (id <NSCopying>)self;
        instance = instances[key];
        if (instance == nil) {
            //Break allocation and initialization into two statements to prevent a stack overflow, if init somehow calls the sharedInstance method
            id allocatedInstance = [self alloc];

            //Store the instance into the dictionary, one per concrete class (class acts as key for the dictionary)
            //Do this right after allocation to avoid the stackoverflow problem
            if (allocatedInstance != nil) {
                instances[key] = allocatedInstance;
            }
            instance = [allocatedInstance init];

            //Following code may be overly cautious
            if (instance != allocatedInstance) {
                //Somehow the init method did not return the same instance as the alloc method
                if (instance == nil) {
                    //If init returns nil: immediately remove the instance again
                    [instances removeObjectForKey:key];
                } else {
                    //Else: put the instance in the dictionary instead of the allocatedInstance
                    instances[key] = instance;
                }
            }
        }
    }
    return instance;
}
Werner Altewischer
  • 10,080
  • 4
  • 53
  • 60
-2
#import <Foundation/Foundation.h>

@interface SingleTon : NSObject

@property (nonatomic,strong) NSString *name;
+(SingleTon *) theSingleTon;

@end

#import "SingleTon.h"
@implementation SingleTon

+(SingleTon *) theSingleTon{
    static SingleTon *theSingleTon = nil;

    if (!theSingleTon) {

        theSingleTon = [[super allocWithZone:nil] init
                     ];
    }
    return theSingleTon;
}

+(id)allocWithZone:(struct _NSZone *)zone{

    return [self theSingleTon];
}

-(id)init{

    self = [super init];
    if (self) {
        // Set Variables
        _name = @"Kiran";
    }

    return self;
}

@end

Hope above code will help it out.

kiran
  • 4,285
  • 7
  • 53
  • 98
-2

if you need to create singleton in swift,

class var sharedInstance: MyClass {
    struct Singleton {
        static let instance = MyClass()
    }
    return Singleton.instance
}

or

struct Singleton {
    static let sharedInstance = MyClass()
}

class var sharedInstance: MyClass {
    return Singleton.sharedInstance
}

you can use this way

let sharedClass = LibraryAPI.sharedInstance
muhammedkasva
  • 672
  • 4
  • 12
  • 20