The Singleton Pattern
Objective-C: Singletons
A Singleton is a special kind of class where only one instance of the class exists for the current process. Singletons can be an easy way to share data and common methods across your entire app.
Note: Apple uses this approach a lot. For example:
[NSUserDefaults standardUserDefaults]
, [UIApplication sharedApplication]
, [UIScreen mainScreen]
, [NSFileManager defaultManager]
all return a Singleton object.How to Use the Singleton Pattern
Take a look at the diagram below:
@interface LibraryAPI : NSObject
+ (LibraryAPI*)sharedInstance;
@end
+ (LibraryAPI*)sharedInstance
{
// 1
static LibraryAPI *_sharedInstance = nil;
// 2
static dispatch_once_t oncePredicate;
// 3
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[LibraryAPI alloc] init];
});
return _sharedInstance;
}
There’s a lot going on in this short method:
- Declare a static variable to hold the instance of your class, ensuring it’s available globally inside your class.
- Declare the static variable
dispatch_once_t
which ensures that the initialization code executes only once. - Use Grand Central Dispatch (GCD) to execute a block which initializes an instance of
LibraryAPI
. This is the essence of the Singleton design pattern: the initializer is never called again once the class has been instantiated.
The next time you call
sharedInstance
, the code inside the dispatch_once
block won’t be executed (since it’s already executed once) and you receive a reference to the previously created instance of LibraryAPI
.@synchronized vs dispatch_once
http://blog.bjhomer.com/2011/09/synchronized-vs-dispatchonce.html [Reference link]
It looks like dispatch_once is about 2x faster than @synchronized
Each test accessed the singleton object 10 million times. I ran both single-threaded tests and multi-threaded tests. Here were the results:
Each test accessed the singleton object 10 million times. I ran both single-threaded tests and multi-threaded tests. Here were the results:
Single threaded results
-----------------------
@synchronized: 3.3829 seconds
dispatch_once: 0.9891 seconds
Multi threaded results
----------------------
@synchronized: 33.5171 seconds
dispatch_once: 1.6648 seconds
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;
@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.
Comments
Post a Comment