Why Does Apple Recommend to Use Dispatch_Once for Implementing the Singleton Pattern Under Arc

Why does Apple recommend to use dispatch_once for implementing the singleton pattern under ARC?

dispatch_once() is absolutely synchronous. Not all GCD methods do things asynchronously (case in point, dispatch_sync() is synchronous). The use of dispatch_once() replaces the following idiom:

+ (MyClass *)sharedInstance {
static MyClass *sharedInstance = nil;
@synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[MyClass alloc] init];
}
}
return sharedInstance;
}

The benefit of dispatch_once() over this is that it's faster. It's also semantically cleaner, because it also protects you from multiple threads doing alloc init of your sharedInstance--if they all try at the same exact time. It won't allow two instances to be created. The entire idea of dispatch_once() is "perform something once and only once", which is precisely what we're doing.

Usage of dispatch_once

This is the standard pattern for a shared instance, otherwise known as a faux singleton.

In many cases programmers choose to use a single object that can be easily accessed from any part of an application - by calling a static method which returns back a reference to the shared object, i.e. sharedInstance in your example - as a means to provide communication/shared data between otherwise independent parts of the application.

It is a faux singleton pattern as it does not prevent other instances of the same type - DBHelper in your example - from being created. A true singleton model is one in which only a single instance of the type can ever be created. (Apple used to have sample code showing how to create true singletons, but it was never updated for the post-ARC world, for more details on that including an ARC version see this answer.)

HTH

How does ARC know to retain the singleton object?

In ARC defenition myclass *_instance; mean strong reference at myclass objects. So every assignment to _instance will retain object.
And there is litle correction of yours code:

static myclass *_instance = nil;

+ (myclass *)sharedInstance {
static dispatch_once_t _onceToken = 0;
dispatch_once(&_onceToken, ^{
_instance = [[myclass alloc] init];
});
return _instance;
}

Is dispatch_once overkill inside of +[NSObject initialize]?

Bill Bumgarner says that dispatch_once is Apple's recommended practice now.

Relating to thread and memory-safety of +initialize, thanks to this tweet, I found the relevant runtime sources to check. objc-initialize.mm says:

 * Only one thread is allowed to actually initialize a class and send 
* +initialize. Enforced by allowing only one thread to set CLS_INITIALIZING.

Classes may be initialized on different threads, and objc-initialize.mm has a strategy to avoid them deadlocking:

*  +initialize deadlock case when a class is marked initializing while 
* its superclass is initialized. Solved by completely initializing
* superclasses before beginning to initialize a class.
*
* OmniWeb class hierarchy:
* OBObject
* | ` OBPostLoader
* OFObject
* / \
* OWAddressEntry OWController
* |
* OWConsoleController
*
* Thread 1 (evil testing thread):
* initialize OWAddressEntry
* super init OFObject
* super init OBObject
* [OBObject initialize] runs OBPostLoader, which inits lots of classes...
* initialize OWConsoleController
* super init OWController - wait for Thread 2 to finish OWController init
*
* Thread 2 (normal OmniWeb thread):
* initialize OWController
* super init OFObject - wait for Thread 1 to finish OFObject init
*
* deadlock!
*
* Solution: fully initialize super classes before beginning to initialize
* a subclass. Then the initializing+initialized part of the class hierarchy
* will be a contiguous subtree starting at the root, so other threads
* can't jump into the middle between two initializing classes, and we won't
* get stuck while a superclass waits for its subclass which waits for the
* superclass.

Additionally, class initialization state variables, are guarded by a monitor_t, which is actually defined as:

typedef struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
} monitor_t;

Since it is a p_thread_mutex, and p_thread calls implement memory barriers, it is equally safe to use:

static NSObject * Bar;
@implementation Foo
+ (void)initialize {
if (self == [Foo class]) {
Bar = [NSObject new];
}
}
@end

and

static NSObject * Bar;
@implementation Foo
+ (void)initialize {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Bar = [NSObject new];
});
}
@end

Why does the suggested Swift singleton implementation use a struct?

with your implementation, the 'sharedInstance' is not a singleton cause each time it gets called, it creates new instance of MySingleton. And, to create a static variable, you have to put it in struct o enums, otherwise, you will get compiler error

How to properly implement ARC compatible and `alloc init` safe Singleton class?

Apple recommends the strict singleton implementation (no other living object of the same type is allowed) this way:

+ (instancetype)singleton {
static id singletonInstance = nil;
if (!singletonInstance) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singletonInstance = [[super allocWithZone:NULL] init];
});
}
return singletonInstance;
}

+ (id)allocWithZone:(NSZone *)zone {
return [self singleton];
}

- (id)copyWithZone:(NSZone *)zone {
return self;
}

Link to the apple documentation (bottom of page, Without ARC)

Dispatch once (dispatch_once) singleton freezes / locks in objective c

Thanks to the help of alexcurylo, queueing out the loadFromDisk job addressed the issue. In order so that people aren't confused, it just so happens that with this code below I am queuing a job that loads an array (which acts as a saved queue of user requests) from the disk. You could be queueing any job, such as loading images / etc. The following code in my sharedManager class (where load from disk is being called) worked, as is shown here:

-(void) loadQueueFromFileByQueueing
{
//Create a Queue
/* Note that fileStrQueue should be added to the .h file as NSOperationQueue *fileStrQueue;*/
fileStrQueue = [[NSOperationQueue alloc] init];

//Create an operation which will invoke method loadQueueFromDisk
NSInvocationOperation * operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadQueueFromDisk) object:nil];

//Add this operation to the queue
[fileStrQueue addOperation:operation];

//Releasing the above operation as it is already retained by the queue.
// [operation release]; // If you are using ARC, this won't be necessary
}


Related Topics



Leave a reply



Submit