【iOS】巧用Singleton(單例)
一.什麼是單例設計模式
1、簡單說明:
(1)永遠只分配一塊記憶體來建立物件,實現allocWithZone方法
(2)提供一個類方法,返回內部唯一的一個變數
2、單例模式說明
(1)單例模式的作用 :可以保證在程式執行過程,一個類只有一個例項,而且該例項易於供外界訪問,從而方便地控制了例項個數,並節約系統資源。
(2)單例模式的使用場合:在整個應用程式中,共享一份資源(這份資源只需要建立初始化1次),應該讓這個類創建出來的物件永遠只有一個。
(3)單例模式在ARC\MRC環境下的寫法有所不同,需要編寫2套不同的程式碼
可以用巨集判斷是否為ARC環境,後面的使用中會告訴你如何編寫程式碼來適配兩種不同的情況
二.在什麼時候使用單例和使用單例的優缺點:
1.如果在專案中訪問的是同一個物件,那麼我們就應該提供一個全域性的訪問點給外界,此時使用單例可以減少物件的建立次數,節省記憶體的分配
2.建立單例物件可以高效的訪問物件的方法,提高app的執行速率
3.定義單例物件更加符合蘋果MVC的封裝思想,體現出了高內聚,低耦合,程式碼更加簡潔
缺點:
物件被建立以後,由於是單例物件(static修飾的物件),物件一旦建立就不會銷燬,知道app完全意義上的退出才銷燬物件
總結:
單例在專案中的是必不可少的,它可以使我們全域性都可共享我們的資料。這只是簡單的問題,大家根據自己的情況回答。
- 首先,單例寫法有好幾種,通常的寫法是基於執行緒安全的寫法,結合dispatch_once來使用,保證單例物件只會被建立一次。如果不小心銷燬了單例,再呼叫單例生成方法是不會再建立的。
- 其次,由於單例是約定俗成的,因此在實際開發中通常不會去重寫記憶體管理方法。
單例確實給我們帶來的便利,但是它也會有代價的。單例一旦建立,整個App使用過程都不會釋放,這會佔用記憶體,因此不可濫用單例。
那麼,同樣作為工具類的使用,到底是使用單例還是類方法,這個就得看需求,如果說工具類不依賴任何屬性的時候就是用類方法(類方法的使用在此就不展開說了)
三.如何實現單例
1.ARC下實現單例
[objc] view plain copy
- //定義一份變數(整個程式執行過程中,只有一份)
- +(instancetype)shareProductName{
- return [[self alloc]init];
- }
- //重寫該方法,控制記憶體的分配,永遠只分配一次儲存空間
- +(instancetype)allocWithZone:(struct _NSZone *)zone{
- static id instance = nil;
- //裡面的程式碼只會執行一次,
- //補充:如果把程式碼下載dispatch_once裡面,那麼它內部預設會進行加鎖。
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- instance = [super allocWithZone:zone];
- });
- return instance;
- }
- -(instancetype)copyWithZone:(struct _NSZone *)zone{
- return self;
- }
2.MRC下實現單例
專案預設環境是ARC環境,將ARC環境轉換為MRC的操作如下圖:
非ARC中(MRC),單例模式的實現(比ARC多了幾個步驟)
實現記憶體管理方法
- (id)retain { return self; }
- (NSUInteger)retainCount { return 1; }
- (oneway void)release {}
- (id)autorelease { return self; }
[objc] view plain copy
- //定義一份變數(整個程式執行過程中,只有一份)
- +(instancetype)shareProductName{
- return [[self alloc]init];
- }
- //重寫該方法,控制記憶體的分配,永遠只分配一次儲存空間
- +(instancetype)allocWithZone:(struct _NSZone *)zone{
- static id instance = nil;
- //裡面的程式碼只會執行一次,
- //補充:如果把程式碼下載dispatch_once裡面,那麼它內部預設會進行加鎖。
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- instance = [super allocWithZone:zone];
- });
- return instance;
- }
- -(oneway void)release{
- }
- -(instancetype)retain{
- return self;
- }
- -(instancetype)autorelease{
- return self;
- }
- -(NSUInteger)retainCount{
- return 1;
- }
3、把單例程式碼定義為一個帶引數的巨集
1>.新的困擾
弊端:如果又建立一個新的類,是否又要把檔案程式碼拷貝一份,所以這裡可以考慮把固定的程式碼寫成巨集。
由於專案中程式碼經常有移植的需要,要求專案中又有ARC的,又有非ARC的,應該怎麼應用單例模式?
不管專案是ARC的還是非ARC的,這個巨集都有用。可以先判斷編譯器的環境,判斷當前環境是否是ARC的。
條件編譯的使用:
2>.使用條件編譯,並把單例模式的程式碼定義為巨集。
新建一個.h標頭檔案
把程式碼定義為巨集,標頭檔案中的程式碼如下:
[objc] view plain copy
- // ## : 連線字串和引數
- #define CRJSingleton_h(name) + (instancetype)share##name;
- #if __has_feature(objc_arc)//arc環境
- #define CRJSingleton_m(name) + (instancetype)share##name{\
- return [[self alloc]init];\
- }\
- \
- +(instancetype)allocWithZone:(struct _NSZone *)zone{\
- static id instance = nil;\
- static dispatch_once_t onceToken;\
- dispatch_once(&onceToken,^{\
- instance = [super allocWithZone:zone];\
- });\
- return instance;\
- }\
- \
- -(id)copyWithZone:(struct _NSZone *)zone{\
- return self;\
- }
- #else
- #define CRJSingleton_m(name) + (instancetype)share##name{\
- return [[self alloc]init];\
- }\
- \
- +(instancetype)allocWithZone:(struct _NSZone *)zone{\
- static id instance = nil;\
- static dispatch_once_t onceToken;\
- dispatch_once(&onceToken,^{\
- instance = [super allocWithZone:zone];\
- });\
- return instance;\
- }\
- \
- -(id)copyWithZone:(struct _NSZone *)zone{\
- return self;\
- }\
- \
- -(oneway void)release{\
- \
- }\
- \
- -(instancetype)retain{\
- return self;\
- }\
- \
- -(instancetype)autorelease{\
- return self;\
- }\
- \
- -(NSUInteger)retainCount{\
- return 1;\
- }
- #endif
四.怎樣用swift實現單例
swift建立單例的方式有好多種,下面我只寫出最簡潔的版本:
[objc] view plain copy
- //在swift中的單例
- class CRJSongleton: NSObject {
- // 常量保證只執行一次, let是執行緒安全的
- static let sharedInstance: CRJSongleton = CRJSongleton()
- }
為什麼swift中這麼簡單的一句話就可以實現單例呢?
1.static 修飾的變數/常量在建立以後就一直存在,知道程式真正意義上的退出才銷燬,從而達到了物件可以隨時呼叫
2.let 修飾的是不可變的常量,本身就是執行緒安全的,常量可以保證只賦值一次以後不能再被修改,等價於GCD中的只執行一次dispatch_once
綜上所述,用Swift實現單例就是這麼滴簡單,一句程式碼實現物件只允許被建立一次,且全域性都可訪問
原文:http://blog.csdn.net/github_34613936/article/details/51290356