現在網上的有很多人寫單例模式,一個很基本的東西但是版本也有很多,新人看了難免有些眼花繚亂的感覺。自己最新比較閑,也過來寫一些自己的心得。在往下看之前,我們要明白一點,那就是在什麼情況下我們才要用到單例模式呢?單例模式在一般情況下用於當一個類只能有一個實例的時候,或者說當一個類只需要定義一個,而且還要...
現在網上的有很多人寫單例模式,一個很基本的東西但是版本也有很多,新人看了難免有些眼花繚亂的感覺。自己最新比較閑,也過來寫一些自己的心得。
在往下看之前,我們要明白一點,那就是在什麼情況下我們才要用到單例模式呢?單例模式在一般情況下用於當一個類只能有一個實例的時候,或者說當一個類只需要定義一個,而且還要被重覆使用的時候將它定義成為單例是最好的。(例如視頻播放器,音頻播放器等工具類用用單例模式加以控制是非常合適的)
在創建一個單例之前,我們還需要知道一點,那就是我們創建一個單例,我們的最終目的是什麼呢?
單例模式需要達到的目的:
1. 封裝一個共用的資源
2. 提供一個固定的實例創建方法
3. 提供一個標準的實例訪問介面
好了,接下來我們就要開始了,為了規範我們應該需要知道創建一個單例具體是有哪幾個步驟呢?
在iOS中我們創建一個單例類,我們需要做3個步驟
1、先為我們要做的單例創建一個靜態實例,並初始化它,然後設置成為nil;
2、在下麵的實例構造方法中檢查在第1步中聲明的靜態實例是否為nil,若判斷為真,那麼就新建一個並且返回一個本例的實例;
3、重載所有涉及到allocation的方法,對allocWithZone,copyWithZone,release以及autorelease進行重載,這樣的話即使在別的地方使用alloc和init方法創建該類的話也不會再產生一個新的實例了;
單例模式的創建
假設以創建一個PlayViewController的單例模式為例:
1、首先創建一個靜態實例
1 static PlayViewController *PlayManager = nil;
2、然後為其添加一個類方法
1 static PlayViewController *PlayManager = nil;
2+ ( PlayViewController *)defaultManager{
3 @synchronized(self) {
4 if(PlayManager == nil) {
5 [[[self class] alloc] init];
6 }
7 }
8 return PlayManager;
9 }
回顧總結:
a、用到了關鍵字@synchronized是為了保證我們的單例的線程級別的安全,可以適用於多線程模式下。
b、static變數PlayManager用於存儲一個單例的指針,並且強制所有對該變數的訪問都必須通過類方法 +(id)defaultManager,在對 +(id)defaultManager第一次調用時候完成實例的創建。
c、上面代碼中用的是[[self class] alloc],而不是 [PlayViewController alloc],一般情況下這兩種寫法產生同樣的效果,但是這裡這樣做是為了更好的利用OOP的性質,[self class]可以動態查找並確定類的類型從而便於實現對該類的子類化。
3、這個時候創建的單例並不能說是真正意義上的單例,因為他還不具備單例的單態性,所以我們還要通過一些方法來避免單例被多次重覆創建。也就是說當用戶不使用+(id)defaultManager創建對象,而是使用alloc方法創建對象時,就會又生成一個對象實例,這跟我們最初只想創建一個單例的想法相衝突,那麼我們怎麼辦呢?
我們可以看到,在方法+(PlayViewController *)defaultManager中只是解決了單例的創建和訪問,但是並不能限制其他地方的代碼通過alloc方法來創建更多的實例,所以,所有涉及到allocation的方法我們都需要進行重載,這些方法包括+(id)alloc、+(id)allocWithZone、-(id)copyWithZone。
1 + (id)alloc
2 {
3 @synchronized(self) {
4 if(PlayManager == nil) {
5 PlayManager = [super alloc];
6 }
7 return PlayManager;
8 }
9 }
1 +(id)allocWithZone:(NSZone*)zone
2 {
3 @synchronized(self) {
4 if(PlayManager == nil) {
5 PlayManager = [super allocWithZone:zone];
6 }
7 return PlayManager;
8 }
9 }
1 - (id)copyWithZone:(NSZone *)zone
2 {
3 return self;
4 }
回顧總結:
很多時候,我們會發現在有的單例裡面對於alloc方法,並沒有進行重載,而只是單純的重載了allocWithZone方法。這是為什麼呢?
因為重載allocWithZone是一個比較全面的方法,在使用alloc方法時,alloc方法自身會調用allocWithZone這個方法。而使用allocWithZone時則不會調用alloc方法。
所以很多時候沒必要重寫alloc,直接重寫allocWithZone即可。(也就是說只要定義一個allocWithZone就可以了)
上述的代碼只是在ARC中可以正確使用,如果實在MRC中,有retain,copy,release, autorelease,這些方法都會使得引用計數變化,所以,我們都需要對這些方法重寫。
1 - (id)retain
2 {
3 return self;
4 }
1 - (id)copy
2 {
3 return self;
4 }
1- (oneway void) release
2{
3
4}
1- (id) autorelease
2{
3 return self;
4}
1- (NSUInteger) retainCount
2{
3 return 1;
4}
1- (id)init
2{
3 @synchronized(self) {
4 [super init];//往往放一些要初始化的變數.
5 return self;
6 }
7 }
好了,到這裡為止,一個單例的創建算是正式結束了。
謝謝大家的閱讀,如果發現有什麼不妥當的地方,還請大家留言指出問題!!!