How do I implement an Objective-C singleton that is compatible with ARC?
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.
Related videos on Youtube
cescofry
Updated on July 22, 2022Comments
-
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?
-
cescofry over 10 yearsI recently found an Article from Matt Galloway going quite in depth on Singletons for both ARC and manual memory management environments. galloway.me.uk/tutorials/singleton-classes
-
-
Christopher Pickslay over 12 yearsYou just don't do any of the memory management hokey pokey Apple used to recommend in developer.apple.com/library/mac/documentation/Cocoa/Conceptual/…
-
Yogev Shelly almost 12 yearswhat is onceToken? i understand the need for a token but what is it? an enum? can i type what ever...
-
Nick Forge almost 12 years@YogevShelly
dispatch_once_t
is defined indispatch/once.h
, and it's typedefed as along
. 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 almost 12 yearsI'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 almost 12 years@MakingScienceFictionFact, you might want to take a look at this post
-
David almost 12 yearsWhy isn't
static MyClass *sharedInstance = nil;
be placed outside of the method? -
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 thesharedInstance
or something, then you would need to place it outside of the method. -
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 almost 12 years@David
static
variables declared within a method/function are the same as astatic
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 samesharedInstance
variable. -
David almost 12 years@NickForge I did some testing and it is what you say. Thank you.
-
Ricardo Sanchez-Saez over 11 yearsWhat 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 about 11 yearsTrue/False: The
dispatch_once()
bit means that you won't get additional instances, even in the first example...? -
meaning-matters about 11 yearsBecause
static
variables are0
(ornil
) by default, the= nil
is redundant. -
jscs about 11 years@Olie: False, because client code can do
[[MyClass alloc] init]
and bypass thesharedInstance
access. DongXu, you should look at Peter Hosey's Singleton article. If you're going to overrideallocWithZone:
to prevent more instances from being created, you also should overrideinit
to prevent the shared instance from being re-initialized. -
Olie about 11 yearsOk, that's what I thought, hence the
allocWithZone:
version. Thx. -
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 over 10 yearsWon't this result in
c1
being an instance ofAAA
's superclass? You need to call+alloc
onself
, not onsuper
. -
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 pointsself
class. If you want to get super-class object, you need to get runtime reflection functions. -
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 finallyAAA
instance will be allocated. -
Nick Forge over 10 yearsyou're correct, I'd forgotten the subtleties of how
super
works in class methods. -
occulus over 10 yearsPersonally, 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 over 10 yearsThis completely breaks the contract of allocWithZone.
-
occulus over 10 yearsFor more discussion on "to Singleton or not", see boredzo.org/blog/archives/2009-06-17/doing-it-wrong
-
occulus over 10 yearsIf 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 over 10 yearssingleton just means "only one object in memory at any time",this is one thing,being re-initialized is another thing.
-
Ryan Heitner over 9 yearsRemember to use #import <objc/objc-runtime.h>
-
Ky - over 8 yearsTo make this work with
allocWithZone:
, you should keep a dictionary ofMyClass
objects, where the keys are theNSZone
s. -
Chuck Krutsinger almost 6 yearsIf 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.