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
No Umbrella Header Found for Target, Module Map Will Not Be Generated
How to Pass Multiple Values with a Notification in Swift
How to Convert a Double into a Byte Array in Swift
Ios: Sample Code for Simultaneous Record and Playback
Adding Thousand Separator to Int in Swift
Default Keyword in Swift Parameter
Check If a Uialertview Is Showing
Google Sign-In Crashes on iOS 9 Attempting to Call Canopenurl
Allow Only Alphanumeric Characters for a Uitextfield
Dismiss Keyboard on Touch Anywhere Outside Uitextfield
How to Change Device Volume on iOS - Not Music Volume
Record and Play Audio Simultaneously
Place Images Along a Bezier Path
iOS 5 Twitter Framework: Tweeting Without User Input and Confirmation (Modal View Controller)