iOS開發-單例模式的解讀,ios開發解讀
現在網上的有很多人寫單例模式,一個很基本的東西但是版本也有很多,新人看了難免有些眼花繚亂的感覺。自己最新比較閑,也過來寫一些自己的心得。
在往下看之前,我們要明白一點,那就是在什麼情況下我們才要用到單例模式呢?單例模式在一般情況下用於當一個類只能有一個執行個體的時候,或者說當一個類只需要定義一個,而且還要被重複使用的時候將它定義成為單例是最好的。(例如視頻播放器,音頻播放器等工具類用用單例模式加以控制是非常合適的)
在建立一個單例之前,我們還需要知道一點,那就是我們建立一個單例,我們的最終目的是什麼呢?
單例模式需要達到的目的:
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 }
好了,到這裡為止,一個單例的建立算是正式結束了。
謝謝大家的閱讀,如果發現有什麼不妥當的地方,還請大家留言指出問題!!!