How do I implement an Objective-C singleton that is compatible with ARC?

95,350

Solution 1

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;
}

Solution 2

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;
}

Solution 3

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

Solution 4

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

Solution 5

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.

Share:
95,350

Related videos on Youtube

cescofry
Author by

cescofry

Updated on July 22, 2022

Comments

  • cescofry
    cescofry almost 2 years

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

  • Christopher Pickslay
    Christopher Pickslay over 12 years
    You just don't do any of the memory management hokey pokey Apple used to recommend in developer.apple.com/library/mac/documentation/Cocoa/Conceptu‌​al/…
  • Yogev Shelly
    Yogev Shelly almost 12 years
    what is onceToken? i understand the need for a token but what is it? an enum? can i type what ever...
  • Nick Forge
    Nick Forge almost 12 years
    @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.
  • Aaronium112
    Aaronium112 almost 12 years
    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?
  • kervich
    kervich almost 12 years
    @MakingScienceFictionFact, you might want to take a look at this post
  • David
    David almost 12 years
    Why isn't static MyClass *sharedInstance = nil; be placed outside of the method?
  • Nick Forge
    Nick Forge almost 12 years
    @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.
  • David
    David almost 12 years
    @NickForge but if you were to call shared instance multiple times, wouldn't the shared instance be two different objects?
  • Nick Forge
    Nick Forge almost 12 years
    @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.
  • David
    David almost 12 years
    @NickForge I did some testing and it is what you say. Thank you.
  • Ricardo Sanchez-Saez
    Ricardo Sanchez-Saez over 11 years
    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).
  • Olie
    Olie about 11 years
    True/False: The dispatch_once() bit means that you won't get additional instances, even in the first example...?
  • meaning-matters
    meaning-matters about 11 years
    Because static variables are 0 (or nil) by default, the = nil is redundant.
  • jscs
    jscs about 11 years
    @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. 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.
  • Olie
    Olie about 11 years
    Ok, that's what I thought, hence the allocWithZone: version. Thx.
  • eonil
    eonil over 10 years
    @RicardoSánchez-Sáez You can delete +alloc~ method to prevent external instantiation completely. I posted an example as an answer.
  • Nick Forge
    Nick Forge over 10 years
    Won't this result in c1 being an instance of AAA's superclass? You need to call +alloc on self, not on super.
  • eonil
    eonil over 10 years
    @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
    eonil over 10 years
    @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.
  • Nick Forge
    Nick Forge over 10 years
    you're correct, I'd forgotten the subtleties of how super works in class methods.
  • occulus
    occulus over 10 years
    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
    occulus over 10 years
    This completely breaks the contract of allocWithZone.
  • occulus
    occulus over 10 years
    For more discussion on "to Singleton or not", see boredzo.org/blog/archives/2009-06-17/doing-it-wrong
  • occulus
    occulus over 10 years
    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 boredzo.org/blog/archives/2009-06-17/doing-it-wrong
  • DongXu
    DongXu over 10 years
    singleton just means "only one object in memory at any time",this is one thing,being re-initialized is another thing.
  • Ryan Heitner
    Ryan Heitner over 9 years
    Remember to use #import <objc/objc-runtime.h>
  • Ky -
    Ky - over 8 years
    To make this work with allocWithZone:, you should keep a dictionary of MyClass objects, where the keys are the NSZones.
  • Chuck Krutsinger
    Chuck Krutsinger almost 6 years
    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.