1. 程式人生 > >【iOS】巧用Singleton(單例)

【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

  1. //定義一份變數(整個程式執行過程中,只有一份)  
  2. +(instancetype)shareProductName{  
  3.     return [[self alloc]init];  
  4. }  
  5. //重寫該方法,控制記憶體的分配,永遠只分配一次儲存空間  
  6. +(instancetype)allocWithZone:(struct _NSZone *)zone{  
  7.     static id instance = nil;  
  8.     //裡面的程式碼只會執行一次,  
  9.     //補充:如果把程式碼下載dispatch_once裡面,那麼它內部預設會進行加鎖。  
  10.     static dispatch_once_t onceToken;  
  11.     dispatch_once(&onceToken, ^{  
  12.         instance = [super allocWithZone:zone];  
  13.     });  
  14.     return instance;  
  15. }  
  16. -(instancetype)copyWithZone:(struct _NSZone *)zone{  
  17.     return self;  
  18. }  


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

  1. //定義一份變數(整個程式執行過程中,只有一份)  
  2. +(instancetype)shareProductName{  
  3.     return [[self alloc]init];  
  4. }  
  5. //重寫該方法,控制記憶體的分配,永遠只分配一次儲存空間  
  6. +(instancetype)allocWithZone:(struct _NSZone *)zone{  
  7.     static id instance = nil;  
  8.     //裡面的程式碼只會執行一次,  
  9.     //補充:如果把程式碼下載dispatch_once裡面,那麼它內部預設會進行加鎖。  
  10.     static dispatch_once_t onceToken;  
  11.     dispatch_once(&onceToken, ^{  
  12.         instance = [super allocWithZone:zone];  
  13.     });  
  14.     return instance;  
  15. }  
  16.   
  17. -(oneway void)release{  
  18.       
  19. }  
  20.   
  21. -(instancetype)retain{  
  22.     return self;  
  23. }  
  24. -(instancetype)autorelease{  
  25.     return self;  
  26. }  
  27. -(NSUInteger)retainCount{  
  28.     return 1;  
  29. }  

 

3、把單例程式碼定義為一個帶引數的巨集

1>.新的困擾

弊端:如果又建立一個新的類,是否又要把檔案程式碼拷貝一份,所以這裡可以考慮把固定的程式碼寫成巨集。

由於專案中程式碼經常有移植的需要,要求專案中又有ARC的,又有非ARC的,應該怎麼應用單例模式?

不管專案是ARC的還是非ARC的,這個巨集都有用。可以先判斷編譯器的環境,判斷當前環境是否是ARC的。

條件編譯的使用:

2>.使用條件編譯,並把單例模式的程式碼定義為巨集。

新建一個.h標頭檔案

  

把程式碼定義為巨集,標頭檔案中的程式碼如下:

 

[objc] view plain copy

  1. // ## : 連線字串和引數  
  2. #define CRJSingleton_h(name) + (instancetype)share##name;  
  3.   
  4. #if __has_feature(objc_arc)//arc環境  
  5. #define CRJSingleton_m(name) + (instancetype)share##name{\  
  6. return [[self alloc]init];\  
  7. }\  
  8. \  
  9. +(instancetype)allocWithZone:(struct _NSZone *)zone{\  
  10. static id instance = nil;\  
  11. static dispatch_once_t onceToken;\  
  12. dispatch_once(&onceToken,^{\  
  13. instance = [super allocWithZone:zone];\  
  14. });\  
  15. return instance;\  
  16. }\  
  17. \  
  18. -(id)copyWithZone:(struct _NSZone *)zone{\  
  19. return self;\  
  20. }  
  21.   
  22. #else  
  23. #define CRJSingleton_m(name) + (instancetype)share##name{\  
  24. return [[self alloc]init];\  
  25. }\  
  26. \  
  27. +(instancetype)allocWithZone:(struct _NSZone *)zone{\  
  28. static id instance = nil;\  
  29. static dispatch_once_t onceToken;\  
  30. dispatch_once(&onceToken,^{\  
  31. instance = [super allocWithZone:zone];\  
  32. });\  
  33. return instance;\  
  34. }\  
  35. \  
  36. -(id)copyWithZone:(struct _NSZone *)zone{\  
  37. return self;\  
  38. }\  
  39. \  
  40. -(oneway void)release{\  
  41. \  
  42. }\  
  43. \  
  44. -(instancetype)retain{\  
  45. return self;\  
  46. }\  
  47. \  
  48. -(instancetype)autorelease{\  
  49. return self;\  
  50. }\  
  51. \  
  52. -(NSUInteger)retainCount{\  
  53. return 1;\  
  54. }  
  55. #endif  

 

 

 

四.怎樣用swift實現單例

swift建立單例的方式有好多種,下面我只寫出最簡潔的版本:

 

 

[objc] view plain copy

  1. //在swift中的單例  
  2.   
  3. class CRJSongleton: NSObject {  
  4.       
  5.     // 常量保證只執行一次, let是執行緒安全的  
  6.     static let sharedInstance: CRJSongleton = CRJSongleton()  
  7. }  


為什麼swift中這麼簡單的一句話就可以實現單例呢?

 

1.static 修飾的變數/常量在建立以後就一直存在,知道程式真正意義上的退出才銷燬,從而達到了物件可以隨時呼叫

2.let 修飾的是不可變的常量,本身就是執行緒安全的,常量可以保證只賦值一次以後不能再被修改,等價於GCD中的只執行一次dispatch_once

綜上所述,用Swift實現單例就是這麼滴簡單,一句程式碼實現物件只允許被建立一次,且全域性都可訪問

 

原文:http://blog.csdn.net/github_34613936/article/details/51290356