无论喜欢与否,有时你确实需要使用单例。事实上,每一个 iOS 和 Mac OS 应用都至少用到了一个单例:UIApplication 或NSApplication。
Love them or loathe them, sometimes you need to have a singleton. In fact every iOS and Mac OS application has at least one, UIApplication or NSApplication.
确实如此的话,那单例又是个什么东西呢? Wikipedia定义如下:
So what is a singleton? Wikipedia defines it as:
在软件工程学中,单例模式是一种设计模式用于实现数学上的单元素集合的概念,它是通过将一个类的实例限定到唯一的一个对象来实现的。
In software engineering, the singleton pattern is a design pattern used to implement the mathematical concept of a singleton, by restricting the instantiation of a class to one object.
或者按我的话说:
Or as I would put it:
单例是一个类,这个类只有一个实例被实例化。
A singleton is a class, where only one instance of it can instantiated.
尽管这是单例的实际定义,但在基础库世界中并不总是这种状况。以NSFileManger 和NSNotificationCenter 为例,通常通过它们的类方法defaultManager 和defaultCenter 被分别访问。尽管没有限制一个单例,但它们的类方法确总是返回一个共享的该类的实例,以例开发者随后通过代码能够访问。这就是我们本次将会看到的这种方法。
Although this is the actual definition of a singleton, this isn’t always the case in the world of Foundation.NSFileManger and NSNotificationCenter for example, are usually accessed through their class methodsdefaultManager and defaultCenter respectively. Although not strictly a singleton, these class methods return a shared instance of that class that developers can then access throughout their code. It is this approach that we will be looking at in this post.
有关使用 Objective-C 实现单例设计模式的最佳方式总是存在着争议,而且包括苹果在内的开发者看似一直在每过几年就会改变他们的想法。当苹果发布 Grand Central Dispatch (GCD) (在 Mac OS 10.6 和 iOS 4.0 中)后,引入了一个函数,非常适合用于实现单例设计模式。
There has always been a debate on the best way to implement the singleton pattern using Objective-C, and developers (including Apple) seem to have been changing their minds every couple of years. When Apple introduced Grand Central Dispatch (GCD) (in Mac OS 10.6 and iOS 4.0) they introduced a function that is perfect for implementing the singleton pattern.
这个函数就是 dispatch_once :
This function is dispatch_once:
void dispatch_once(
dispatch_once_t *predicate,
dispatch_block_t block);
This function takes a predicate (which is a long, that in reality acts as a BOOL) that the dispatch_once function uses to check if the block has already been dispatched. It also takes the block that you wish to only be dispatched once for the lifetime of the application, for us this is the instantiation of our shared instance.
Not only does dispatch_once mean that your code will only ever get run once, it is also thread safe, which means you don’t have to bother with using anything like @synchronized to stop things getting out of sync when using multiple threads and/or queues.
This is verified by Apple’s GCD Documentation:
If called simultaneously from multiple threads, this function waits synchronously until the block has completed.
So how would you use this in practise?
Well lets say you have a Account Manager class, and you want to access a shared instance of this class throughout your application. You can simply implement a class method like the one below:
+ (AccountManager *)sharedManager
{
static AccountManager *sharedAccountManagerInstance = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
sharedAccountManagerInstance = [[self alloc] init];
});
return sharedAccountManagerInstance;
}
This means whenever you want access this shared instance all you need to do is:
AccountManager *accountManager = [AccountManager sharedManager];
And that’s all there is to it, you now have a shared instance that you can access throughout your application, which will only be created once.
This approach has many advantages:
- It is thread safe
- It will keep the static analyser happy
- It is compatible with Automatic Reference Counting (ARC)
- It only requires a small amount of code
The only disadvantage with this approach is that it will still allow a non shared instance to be created:
AccountManager *accountManager = [[AccountManager alloc] init];
Sometimes you will actually want this behaviour, but it is something you need to be aware of when you really only want one instance of a class to ever be instantiated.