【设计模式】五、单例模式(独一无二的对象)
一、概述:
有一些對象我們只需要一個,比方說:線程池(threadpool)、緩存(cache)、對話框、處理偏好設置和注冊表對象、日志對象、充當打印機、顯卡等設備的驅動程序的對象。事實上這些對象只需要一個實例,如果制造出多個實例就會導致很多問題發生。(利用靜態類變量、靜態方法和適當的訪問修飾符的確也可以做到只存在一個實例。)
蘇格拉底誘導式回答:(參考《Head First 設計模式》)
| 如何創建一個對象? | new MyObject() |
| 萬一另外一個對象想創Myobject會怎樣?可以再次new MyObject嗎? | 是的,當然可以。 |
| 所以一旦有一個類,我們是否都能多次的實例化它? | 如果是公開的類 就可以 |
| 如果不是的話,會怎樣? | 如果不是公開的類,只有同一個包內的類可以實例化它,但是仍可以實例化它很多次 |
| 可以這么做嗎? public Myclass {private Myclass(){} }? | ?我沒想過,但是這是合法的定義,有一定的道理。 |
| ?怎么說呢? | ?我認為含有私有的構造器類不能唄實例化。 |
| ?又可以使用私有的構造器對象嗎? | ?恩,我想Myclass內的代碼是唯一能調用此構造器的代碼,但是這又不太合乎常理。 |
| ?WHY? | ?因為只有Myclass的實例才能調用Myclass的構造器,但是因為沒有其他類能夠實例化Myclass,所以我們得不到這樣的實例。 |
| ?嘿,我有個想法。 你認為如何? public Myclass {public static MyClass getInstance(){} }MyClass有一個靜態方法,我們可以這樣調用這個方法: MyClas.getInstance(); | ? |
| ?為何調用的時候用MyCLass類名,而不是對象名。 | ?因為getInstance是類方法,是一個靜態方法,你需要使用類名。 |
| ?有意思,假如把這些合在一起“是否”就可以初始化一個MyClass? public MyClass {private MyClass(){}public static Myclass getInstance(){return new MyClass();} }? | 當然可以? |
| ?好了,你能想出第二種實例化對象的方式嗎? | ?MyClass.getInstance(); |
| ?你能夠完成代碼是MyClass只有一個實例被產生嗎? | ?恩,大概可以吧。。。。 |
?
單例模式優點
單例模式缺點
二、在IOS中的應用
單例模式在iOS開發中的使用還是蠻多的,許多Foundation、Cocoa和UIKit中的類都實現了單例模式,比如應用程序本身UIApplication、文件操作類NSFileManager、消息中心NSNotificitonCenter等系統都已經給我們實現單例,我們只需要使用就好了。在iOS中使用單例模式要使用類方法,通過類方法返回該類的唯一對象。
在ios中的應用主要有以下三種方式
1、
static Singleton *instance = nil;+ (Singleton *)sharedInstance {if (instance == nil) {instance = [[super allocWithZone:NULL] init];}return instance; }+ (id)allocWithZone:(NSZone *)zone {return [[self sharedInstance] retain]; }- (id)copyWithZone:(NSZone *)zone {return self; }- (id)retain {return self; }- (NSUInteger)retainCount {return NSUIntegerMax; //denotes an object that cannot be released }- (void)release {//do nothing }- (id)autorelease {return self; }可以看到這種方式,使用靜態成員維持了一個永久存在的對象,而且覆蓋了alloc方法(alloc方法會調用allocWithZone:方法),并且也覆蓋了所有與引用技術有關的方法,這都使這個對象不會被銷毀。這樣看上去基本實現了我們需要的,但是寫起來麻煩不說,還有很大的一個問題,那就是多線程問題,如果是在多線程中那么該種方法就不能保證只產生一個對象了。所以這種方式只是介紹一下,并不推薦使用。
2、引入頭文件
程序員都是偷懶的,現在流行使用一個宏定義來搞定這許多的事,而且考慮的更加周全。
單例包含以下接口?
+ (MyClass*) sharedInstance;?
+ (void) purgeSharedInstance;
調用sharedInstance會創建并返回單例
調用purgeSharedInstance會銷毀單例
手動調用alloc也可以保證是單例,你可以這樣調用
[[MyClass alloc] initWithParam:firstParam secondParam:secondParam];
只是要保證在sharedInstance之前調用,因為只有一次創建機會。
下面是使用宏的寫法“?
MyClass.h: ======================================== #import "SynthesizeSingleton.h" @interface MyClass: SomeSuperclass { ... } SYNTHESIZE_SINGLETON_FOR_CLASS_HEADER(MyClass); @end ======================================== MyClass.m: ======================================== #import "MyClass.h" @implementation MyClass SYNTHESIZE_SINGLETON_FOR_CLASS(MyClass); ... @end ========================================開源庫下載地址
3、
iOS在4.0以后推出了block和GCD,這兩個特性給iOS開發帶來的很大的便利,也使開發變得更加趣味話。那么如何通過GCD+block來實現單例模式呢,這主要歸功于dispatch_once(dispatch_once_t *predicate, ^(void)block)這個GCD的函數,他有兩個參數第一參數是一個指向dispatch_once_t類型結構體的指針,用來測試block是否執行完成,該指針所指向的結構體必須是全局的或者靜態的,第二個參數是一個返回值與參數均為空的block,在block體中進行對象的初始化即可。dispatch_once在程序的生命周期中保證只會被調用一次,所以在多線程中也不會有問題。 該種方法使用方法:
?
+ (Singleton *)sharedInstance {static Singleton *instance = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{instance = [[Singleton alloc]init];});return instance; }dispatch_once的作用就是執行且在整個程序的聲明周期中,僅執行一次某一個block對象。簡直就是為單例而生的嘛。而且,有些我們需要在程序開頭初始化的動作,如果為了保證其,僅執行一次,也可以放到這個dispatch_once來執行。
然后我們看到它需要一個斷言來確定這個代碼塊是否執行,這個斷言的指針要保存起來,相對于第一種方法而言,還需要多保存一個指針。
?
方法簡介中就說的很清楚了:對于在應用中創建一個初始化一個全局的數據對象(單例模式),這個函數很有用。
如果同時在多線程中調用它,這個函數將等待同步等待,直至該block調用結束。
這個斷言的指針必須要全局化的保存,或者放在靜態區內。使用存放在自動分配區域或者動態區域的斷言,dispatch_once執行的結果是不可預知的。
?
總結:1.這個方法可以在創建單例或者某些初始化動作時使用,以保證其唯一性。2.該方法是線程安全的,所以請放心大膽的在子線程中使用。(前提是你的dispatch_once_t?onceToken
對象必須是全局或者靜態對象。這一點很重要,如果不能保證這一點,也就不能保證該方法只會被執行一次。)
?
參考博客:
水滴石穿 Keeping faith.??????--- wtlucky's Blog
?
轉載于:https://www.cnblogs.com/ymonke/p/3513668.html
總結
以上是生活随笔為你收集整理的【设计模式】五、单例模式(独一无二的对象)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 传智168期JavaEE struts2
- 下一篇: 【.NET特供-第三季】ASP.NET