Thread Safe Singleton in iOS6 - ios

Im trying to get an updated version of the Singleton Design Pattern which is thread safe. Here is one version that I know. However, I cannot make it work in iOS6
Here is what Im trying to do:
Here is my Class method
+(id)getSingleton
{
static dispatch_once_t pred;
static EntryContainerSingleton *entriesSingleton = nil;
dispatch_once(&pred, ^{
entriesSingleton = [[super alloc] init];
});
return entriesSingleton;
}
+(id)alloc
{
#synchronized([EntryContainerSingleton class])
{
NSLog(#"inside alloc of EntryContainerSingleton");
ERROR >>>>> NSAssert(entriesSingleton == nil, #"Attempted to allocate a second instance of a singleton.");
ERROR >>>>> entriesSingleton = [super alloc];
ERROR >>>>> return entriesSingleton;
}
return nil;
}
-(id)init
{
self = [super init];
......Some custom INitialization
return self;
}
This code throws an error as marked above. The error message says Use of undeclared identifier. In addition the link above recommends the use of
[[allocWithZone:nil] init]
When I use it like this it complains
+(id)allocWithZone:(NSZone*)zone
{
return [self instance];
}
After hours of trying to make it work. It would be great if someone could point out how to do this right. Ive spent much time googling and haven't found a complete implementation example.
Thanks

Why not just use +initialize?
static MyClass *gSingleton;
+(void) initialize {
if ( self == [MyClass class] ) {
gSingleton = [MyClass new];
}
}
+(MyClass*) getSingleton {
return gSingleton;
}
It's thread-safe. The only issue is that it doesn't prevent someone from allocating a second object by using alloc/init or new.

Related

How properly assign singleton to a variable in iOS?

I have a project written in Objective-C. Inside it I use singleton.
Its declaration is:
+ (id)sharedInstance
{
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
I init it inside each class where I use it by this code:
singApp = [XXXsingApplication sharedInstance];
When I run Xcode Analyzer I get following warning:
Instance variable used while 'self' is not set to the result of
'[(super or self) init...]'
Why I get this warning?
I suppose I have some general misunderstanding of matter, because AFAIK it has only single instance and must not be init again to avoid creating several instances?
EDIT:
My init looks like:
-(id)init
{
self = [super init];
// init read-only values
_appSwVersion = APP_SOFT_VERSION;
_appSwDateTime = APP_SOFT_DATETIME;
_websDns = WEBS_DNS;
_testWebsUrl = TEST_WEBS_DNS_URL;
// init properties dictionary
_propListDict = [[NSMutableDictionary alloc] init];
#try {
if (self) {
// load settings
[self loadSettingsFromFile];
// init custom Bundle
[self setCustomBundle];
}
}
#catch (NSException *exception) {
NSLog(#"EXCEPTION\nName-> %#\nDescription-> %#", [exception name], [exception description]);
}
return self;
}
Exceptions are there to catch programming errors. You don't catch and handle exceptions in Objective-C. When an exception happens, you let it crash. This is Objective-C, not Java or C++. You don't handle exceptions, you fix the code.
You should check whether self == nil immediately after calling [super init]. Think about what you are doing: You are assigning to an instance variable which will lead to a crash if self == nil. Sure, there will be cases where people are 100% sure that self cannot be nil. But five lines later, you check whether self == nil. That combination is a clear indication of a bug. If you check, you must check before you assign to an instance variable. If you assign to an instance variable, any check is too late.
Your code should look like this.
-(id)init {
self = [super init];
#try {
if (self) {
// init read-only values
_appSwVersion = APP_SOFT_VERSION;
_appSwDateTime = APP_SOFT_DATETIME;
_websDns = WEBS_DNS;
_testWebsUrl = TEST_WEBS_DNS_URL;
// init properties dictionary
_propListDict = [[NSMutableDictionary alloc] init];
// load settings
[self loadSettingsFromFile];
// init custom Bundle
[self setCustomBundle];
}
} #catch (NSException *exception) {
NSLog(#"EXCEPTION\nName-> %#\nDescription-> %#", [exception name], [exception description]);
}
return self;
}

Singleton object or Use the class for some Build Configuration only - iOS

I have a singleton class. This class has methods which use core data and are to be used only for some build configuration.
+(AClass*) singletonInstance {
static dispatch_once_t dispatchCall;
static AClass *shared=nil;
dispatch_once(&dispatchCall, ^{
shared=[[AClass alloc] init];
});
return shared;
}
-(id)init{
self=[super init]
if(self){
[self initCoreData];
}
return self;
}
So, if I use the following code will this be a correct way to handle it. I think, this will be handled at compile time, so at run time it will be always nil, so any method called on
[[AClass singletonInstance] someMethod]
will not work , As a message passed to a nil object, which will not crash but at the same time, it will not respond to that message. Also, it would not init the core data.
Approach 1
+(AClass*) singletonInstance {
#if DEBUG==6
static dispatch_once_t dispatchCall;
static AClass *shared=nil;
dispatch_once(&dispatchCall, ^{
shared=[[AClass alloc] init];
});
return shared;
#else
return nil;
#endif
}
Approach 2
I have one more helper class which has singleton method. I have this method and is used in many other classes (So, if in future if I change it to DEBUG preprocessor value to say "10", then I will have to make change at one place only). would be ok to use it over here also?
-(BOOL)shouldInitContent{
#if DEBUG==6
return YES;
#else
return NO;
#endif
}
+(AClass*) singletonInstance {
if([[AHelperClass helperInstance] shouldInitContent]){
static dispatch_once_t dispatchCall;
static AClass *shared=nil;
dispatch_once(&dispatchCall, ^{
shared=[[AClass alloc] init];
});
return shared;
}else{
return nil;
}
}
Which of the above two approaches could be efficient?
I would go with approach #1, because it will help you minimize the memory consumption.
In the second approach you are using two singleton instance which I doesn't recommend.

How to initialize a thread-safe variable in `init`?

I have the following code:
{
NSObject *_object;
}
- (instancetype)init {
if (self = [super init]) {
_object = [[NSObject alloc] init];
}
return self;
}
- (NSObject*)object {
return _object;
}
If the method object is called from a second thread after init has completed and returned, how do I know the assignment to _object within init will be visible and it's not actually returning an unassigned pointer?
What is the internal mechanism that guarantees this?
The thread-safety of your code depends on how it is used, and how it is intended to be used is inherently thread-safe. You shouldn't be passing around partially constructed objects, therefore the allocation and initialization ([[... alloc] init] or new) should be confined to a single thread and then passed around to other threads.
Use dispatch_once. This is guaranteed to run only one time no matter how many threads there are. For example
+ (MyClass *)sharedInstance
{
// Static local predicate must be initialized to 0
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[MyClass alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}

Singleton with parameter iOS

I need to implement a singleton class that takes in a parameter. The same object will be passed as a parameter every single time so the resultant singleton object will always be the same.
I am doing something like the code below. Does this look ok? Is there a better way of achieving what I want to achieve?
- (id)sharedInstanceWithAccount:(UserAccount *)userAccount {
if (!sharedInstance) {
#synchronized(self) {
sharedInstance = [[[self class] alloc] initWithAccount:userAccount];
}
}
return sharedInstance;
}
- (id)initWithAccount:(UserAccount *)userAccount {
self = [super init];
if (self) {
_userAccount = userAccount;
}
return self;
}
- (id)init {
NSAssert(false,
#"You cannot init this class directly. It needs UserAccountDataSource as a paramter");
return nil;
}
+ (id)alloc {
#synchronized(self) {
NSAssert(sharedInstance == nil, #"Attempted to allocated a second instance of the singleton");
sharedInstance = [super alloc];
return sharedInstance;
}
return nil;
}
There are a number of problem in this design:
As recommended by Apple, should dispatch_once instead of #synchronized(self) for singleton:
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[MyClass alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
Refer to this question for more detail: Why does Apple recommend to use dispatch_once for implementing the singleton pattern under ARC?
Bad API design to put singleton in alloc.
As indicated by the name of the method alloc, it means that some memory will be allocated. However, in your case, it is not. This attempt to overwrite the alloc will cause confusion to other programmers in your team.
Bad idea to use NSAssert in your -init.
If you want to disable a method, disable it by putting this in your header file:
- (id)init __attribute__((unavailable));
In this case, you will get a compile error instead of crashing the app at run time.
Refer to this post for more detail: Approach to overriding a Core Data property: isDeleted
Moreover, you can even add unavailable message:
- (id)init __attribute__((unavailable("You cannot init this class directly. It needs UserAccountDataSource as a parameter")));
Sometime input parameters is ignored with no warning.
In your following code, how would the programmer who is calling this function know that the input parameter userAccount is sometimes ignored if an instance of the class is already created by someone else?
- (id)sharedInstanceWithAccount:(UserAccount *)userAccount {
if (!sharedInstance) {
#synchronized(self) {
sharedInstance = [[[self class] alloc] initWithAccount:userAccount];
}
}
return sharedInstance;
}
In short, don't think it is a good idea to create singleton with parameter. Use conventional singleton design is much cleaner.
objA = [Object sharedInstanceWithAccount:A];
objB = [Object sharedInstanceWithAccount:B];
B is ignored.
userAccount in objB is A.
if userAccount B in objB, you will change sharedInstanceWithAccount.
- (id)sharedInstanceWithAccount:(UserAccount *)userAccount {
static NSMutableDictionary *instanceByAccount = [[NSMutableDictionary alloc] init];
id instance = instanceByAccount[userAccount];
if (!instance) {
#synchronized(self) {
instance = [[[self class] alloc] initWithAccount:userAccount];
instanceByAccount[userAccount] = instance;
}
}
return instance;
}

class tracking and limiting instances with an NSSet

I'd like my class to detect that a new instance is equivalent (vis a vis isEqual: and hash) to some existing instance, and create only unique instances. Here's code that I think does the job, but I'm concerned it's doing something dumb that I can't spot...
Say it's an NSURLRequest subclass like this:
// MyClass.h
#interface MyClass : NSMutableURLRequest
#end
// MyClass.m
#implementation MyClass
+ (NSMutableSet *)instances {
static NSMutableSet *_instances;
static dispatch_once_t once;
dispatch_once(&once, ^{ _instances = [[NSMutableSet alloc] init];});
return _instances;
}
- (id)initWithURL:(NSURL *)URL {
self = [super initWithURL:URL];
if (self) {
if ([self.class.instances containsObject:self])
self = [self.class.instances member:self];
else
[self.class.instances addObject:self];
}
return self;
}
// Caller.m
NSURL *urlA = [NSURL urlWithString:#"http://www.yahoo.com"];
MyClass *instance0 = [[MyClass alloc] initWithURL: urlA];
MyClass *instance1 = [[MyClass alloc] initWithURL: urlA]; // 2
BOOL works = instance0 == instance1; // works => YES, but at what hidden cost?
Questions:
That second assignment to self in init looks weird, but not insane.
Or is it?
Is it just wishful coding to think that second alloc (of instance1) gets magically cleaned up?
It's not insane, but in manual retain/release mode, you do need to release self beforehand or you'll leak an uninitialized object every time this method is run. In ARC, the original instance will automatically be released for you.
See #1.
BTW, for any readers who usually stop at one answer, bbum's answer below includes a full working example of a thread-safe implementation. Highly recommended for anyone making a class that does this.
Thought of a better way (original answer below the line) assuming you really want to unique by URL. If not, this also demonstrates the synchronization primitive use.
#interface UniqueByURLInstances:NSObject
#property(strong) NSURL *url;
#end
#implementation UniqueByURLInstances
static NSMutableDictionary *InstanceCache()
{
static NSMutableDictionary *cache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cache = [NSMutableDictionary new];
});
return cache;
}
static dispatch_queue_t InstanceSerializationQueue()
{
static dispatch_queue_t queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("UniqueByURLInstances queue", DISPATCH_QUEUE_SERIAL);
});
return queue;
}
+ (instancetype)instanceWithURL:(NSURL*)URL
{
__block UniqueByURLInstances *returnValue = nil;
dispatch_sync(InstanceSerializationQueue(), ^{
returnValue = [InstanceCache() objectForKey:URL];
if (!returnValue)
{
returnValue = [[self alloc] initWithURL:URL];
}
});
return returnValue;
}
- (id)initWithURL:(NSURL *)URL
{
__block UniqueByURLInstances* returnValue = self;
dispatch_sync(InstanceSerializationQueue(), ^{
returnValue = [InstanceCache() objectForKey:URL];
if (returnValue) return;
returnValue = [super initWithURL:URL];
if (returnValue) {
[InstanceCache() setObject:returnValue forKey:URL];
}
_url = URL;
});
return returnValue;
}
- (void)dealloc {
dispatch_sync(InstanceSerializationQueue(), ^{
[InstanceCache() removeObjectForKey:_url];
});
// rest o' dealloc dance here
}
#end
Caveat: Above was typed into SO -- never been run. I may have screwed something up. It assumes ARC is enabled. Yes, it'll end up looking up URL twice when using the factory method, but that extra lookup should be lost in the noise of allocation and initialization. Doing that means that the developer could use either the factory or the initializer and still see unique'd instances but there will be no allocation on execution of the factory method when the instance for that URL already exists.
(If you can't unique by URL, then go back to your NSMutableSet and skip the factory method entirely.)
What Chuck said, but some additional notes:
Restructure your code like this:
+(NSMutableSet*)instances
{
static NSMutableSet *_instances;
dispatch_once( ...., ^{ _instances = [[NSMutableSet alloc] init];});
return instances;
}
Then call that method whenever you want access to instances. It localizes all the code in one spot and isolates it from +initialize (which isn't really a big deal).
If your class may be instantiated from multiple threads, you'll want to surround the check-allocate-or-return with a synchronization primitive. I would suggest a dispatch_queue.

Resources