最近很多文章都在講如何在 Object-C 中實(shí)現(xiàn)單例,但是它們大都沒(méi)有講全。
首先來(lái)看看 單例(Singleton) 的定義:
In software engineering, the singleton pattern is a design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system.
來(lái)自Wikipedia
就是說(shuō),單例模式的目的就是在應(yīng)用程序的生命周期中,限制某個(gè)類的實(shí)例只能有一個(gè)嗜傅。
下面來(lái)看幾個(gè)在 OC 中常用的『?jiǎn)卫环绞健?/p>
方式1
方式1符合那些具有面向?qū)ο缶幊探?jīng)驗(yàn)的程序員一般思維茅诱,因此很自然的會(huì)想到下面的實(shí)現(xiàn)方式:
@interface DownloadManager : NSObject
@end
@implementation DownloadManager
static DownloadManager* _sharedInstance = nil;
+ (DownloadManager*)manager
{
@synchronized(self.class)
{
if (_sharedInstance == nil) {
_sharedInstance = [[self.class alloc] init];
}
return _sharedInstance;
}
}
@end
注意上面代碼中使用了 @synchronized
來(lái)確保在多線程情況下也可以正常的工作逗物。之后我們會(huì)按照『約定』,凡是在需要獲得 DownloadManager
實(shí)例時(shí)瑟俭,統(tǒng)一使用:
DownloadManager* manager = [DownloadManager manager];
方式2
@interface DownloadManager : NSObject
@end
@implementation DownloadManager
+ (DownloadManager*)manager
{
static DownloadManager* instance = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
instance = [[self.class alloc] init];
});
return instance;
}
@end
上面的代碼使用了 dispatch_once
來(lái)確保創(chuàng)建實(shí)例的代碼只會(huì)被運(yùn)行一次翎卓。注意這次我們并沒(méi)有使用 @synchronized
這是因?yàn)?dispatch_once
是 thread-safety,這是 Apple Doc 中的相關(guān)描述:
This function is useful for initialization of global data (singletons) in an application. Always call this function before using or testing any variables that are initialized by the block.
If called simultaneously from multiple threads, this function waits synchronously until the block has completed.
The predicate must point to a variable stored in global or static scope. The result of using a predicate with automatic or dynamic storage (including Objective-C instance variables) is undefined.
同樣的摆寄,之后我們也會(huì)按照約定失暴,凡是在需要獲得 DownloadManager
實(shí)例時(shí),統(tǒng)一使用:
DownloadManager* manager = [DownloadManager manager];
注意到上面的文字描述中微饥,多次強(qiáng)調(diào)了按照『約定』逗扒,為什么這么說(shuō)呢?這是因?yàn)榍烽伲词刮覀兌x了 manager
這個(gè)方法矩肩,但是我們并不能確保調(diào)用者不會(huì)這樣使用:
DownloadManager* manager = [[DownloadManager alloc] init];
所以說(shuō),上面的方式1和方式2實(shí)際上嚴(yán)格來(lái)說(shuō)肃续,并不是真正的『?jiǎn)卫J健?/p>
Swift 中的單例
為了有一個(gè)對(duì)比黍檩,知道什么是嚴(yán)格意義的單例模式叉袍,我們可以看看在 Swift 中如何實(shí)現(xiàn)單例模式:
class DownloadManager {
static let manager = DownloadManager()
private init() { }
}
注意到使用了 private
來(lái)限制了 init
的訪問(wèn)級(jí)別,我們知道在 Swift 中訪問(wèn)控制的最小顆粒度是到文件級(jí)別刽酱,這并不是問(wèn)題喳逛,因?yàn)橥ǔN覀儠?huì)把類定義在一個(gè)單獨(dú)的文件中。
現(xiàn)在在需要獲取 DownloadManager
的實(shí)例時(shí)棵里,只能通過(guò):
let manager = DownloadManager.manager
如果你試圖這樣(注意訪問(wèn)控制的最小顆粒度是文件):
let manager = DownloadManager()
你會(huì)得到一個(gè)錯(cuò)誤提示:
到底是方式1還是方式2
現(xiàn)在我們知道了在 OC 中如果需要實(shí)現(xiàn)單例模式的話润文,有上面的方式1和方式2兩種方式,那么我們應(yīng)該選擇它們中的哪一個(gè)呢衍慎?答案是方式2转唉。
我們選擇方式2的原因有這么幾個(gè):
- 從語(yǔ)義上來(lái)講,方式2更加的清晰明了稳捆,因?yàn)槲覀冎?
dispatch_once
的意思就是對(duì)某個(gè)任務(wù)只執(zhí)行一次赠法,這很符合我們單例的需求。 - 從性能上來(lái)說(shuō)乔夯,
dispatch_once
具有更高的性能砖织。
說(shuō)到性能問(wèn)題,最好還是有 benchmark tests末荐,剛好有人已經(jīng)做了相關(guān)的測(cè)試侧纯,下面就是他測(cè)試的結(jié)果:
Single threaded results
-----------------------
@synchronized: 3.3829 seconds
dispatch_once: 0.9891 seconds
Multi threaded results
----------------------
@synchronized: 33.5171 seconds
dispatch_once: 1.6648 seconds
如果你需要測(cè)試的源碼的話,可以去作者的基hub甲脏。
從上面的測(cè)試中我們看到 dispatch_once
不管在多線程還是單線程情況下眶熬,都要比 @ synchronized
快很多。
作為最后的結(jié)語(yǔ)块请,讓我們擁抱 Swift 吧??娜氏。
總有陽(yáng)光的人給我點(diǎn)贊??