一. 單例模式簡介
- 單例模式的作用
可以保證在程式執行過程,一個類只有一個例項,而且該例項易於供外界訪問
從而方便地控制了例項個數,並節約系統資源 - 單例模式的使用場合
在整個應用程式中,共享一份資源(這份資源只需要建立初始化1次),一般用於工具類。例如:登陸控制器,網路資料請求,音樂播放器等一個工程需要使用多次的控制器或方法。 - 單例模式的優缺點
優點:
單例模式可以保證系統中一個類只有一個例項而且該例項易於外界訪問,從而方便對例項個數的控制並節約系統資源。
如果希望在系統中某個類的物件只能存在一個,單例模式是最好的解決方案。
單例模式因為類控制了例項化過程,所以類可以更加靈活修改例項化過程。
缺點:
單例物件一旦建立,物件指標是儲存在靜態區的,單例物件在堆中分配的記憶體空間,會在應用程式終止後才會被釋放。
單例類無法繼承,因此很難進行類的擴充套件。
單例不適用於變化的物件,如果同一型別的物件總是要在不同的用例場景發生變化,單例就會引起資料的錯誤,不能儲存彼此的狀態。
注意:我們在使用單例類之前,一定要考慮好單例類是否適合和類以後的擴充套件性,避免盲目濫用單例
二. 單例在ARC中的實現
ARC中單例實現步驟
1 在類的內部提供一個static修飾的全域性變數
2 提供一個類方法,方便外界訪問
3 重寫+allocWithZone方法,保證永遠都只為單例物件分配一次記憶體空間
4 嚴謹起見,重寫-copyWithZone方法和-MutableCopyWithZone方法
ARC中單例程式碼實現
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#import "Tools.h" @implementation Tools // 建立靜態物件 防止外部訪問 static Tools *_instance; +(instancetype)allocWithZone:(struct _NSZone *)zone { // @synchronized (self) { // // 為了防止多執行緒同時訪問物件,造成多次分配記憶體空間,所以要加上執行緒鎖 // if (_instance == nil) { // _instance = [super allocWithZone:zone]; // } // return _instance; // } // 也可以使用一次性程式碼 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (_instance == nil) { _instance = [super allocWithZone:zone]; } }); return _instance; } // 為了使例項易於外界訪問 我們一般提供一個類方法 // 類方法命名規範 share類名|default類名|類名 +(instancetype)shareTools { //return _instance; // 最好用self 用Tools他的子類呼叫時會出現錯誤 return [[self alloc]init]; } // 為了嚴謹,也要重寫copyWithZone 和 mutableCopyWithZone -(id)copyWithZone:(NSZone *)zone { return _instance; } -(id)mutableCopyWithZone:(NSZone *)zone { return _instance; } |
三. 單例在MRC中的實現
MRC單例實現步驟
1 在類的內部提供一個static修飾的全域性變數
2 提供一個類方法,方便外界訪問
3 重寫+allocWithZone方法,保證永遠都只為單例物件分配一次記憶體空間
4 嚴謹起見,重寫-copyWithZone方法和-MutableCopyWithZone方法
5 重寫release方法
6 重寫retain方法
7 建議在retainCount方法中返回一個最大值
配置MRC環境
1 注意ARC不是垃圾回收機制,是編譯器特性
2 配置MRC環境:build setting ->搜尋automatic ref->修改為N0
MRC中單例程式碼實現
配置好MRC環境之後,在ARC程式碼基礎上重寫下面的三個方法即可
1 2 3 4 5 6 7 8 9 10 11 12 |
-(oneway void)release { } -(instancetype)retain { return _instance; } -(NSUInteger)retainCount { return MAXFLOAT; } |
四. 一勞永逸,單例模式的優化
如果想要一勞永逸,我們將面臨兩個問題
1:如何寫一份單例程式碼在ARC和MRC環境下都適用?
2:如何使一份單例程式碼可以多個類共同使用
為了解決這兩個問題,我們可以在PCH檔案使用代引數的巨集和條件編譯
利用條件編譯來判斷是ARC還是MRC環境
1 2 3 4 5 |
#if __has_feature(objc_arc) //如果是ARC,那麼就執行這裡的程式碼1 #else //如果不是ARC,那麼就執行代理的程式碼2 #endif |
注意:單例模式不可以使用繼承,因為使用繼承,同時也會繼承靜態變數,當子類和父類同時建立的時候只會建立一個先建立的例項物件。
廢話不多說了直接上程式碼
PCH檔案Single.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#define singleH(name) +(instancetype)share##name; #if __has_feature(objc_arc) #define singleM(name) static id _instance;\ +(instancetype)allocWithZone:(struct _NSZone *)zone\ {\ static dispatch_once_t onceToken;\ dispatch_once(&onceToken, ^{\ _instance = [super allocWithZone:zone];\ });\ return _instance;\ }\ \ +(instancetype)share##name\ {\ return [[self alloc]init];\ }\ -(id)copyWithZone:(NSZone *)zone\ {\ return _instance;\ }\ \ -(id)mutableCopyWithZone:(NSZone *)zone\ {\ return _instance;\ } #else #define singleM static id _instance;\ +(instancetype)allocWithZone:(struct _NSZone *)zone\ {\ static dispatch_once_t onceToken;\ dispatch_once(&onceToken, ^{\ _instance = [super allocWithZone:zone];\ });\ return _instance;\ }\ \ +(instancetype)shareTools\ {\ return [[self alloc]init];\ }\ -(id)copyWithZone:(NSZone *)zone\ {\ return _instance;\ }\ -(id)mutableCopyWithZone:(NSZone *)zone\ {\ return _instance;\ }\ -(oneway void)release\ {\ }\ \ -(instancetype)retain\ {\ return _instance;\ }\ \ -(NSUInteger)retainCount\ {\ return MAXFLOAT;\ } #endif |
這時我們就可以一勞永逸,任何專案中,當我們要使用單例類的時候只要在專案中匯入PCH檔案然後
在.h檔案中呼叫singleH(類名)
在.m檔案中呼叫singleM(類名)
建立類時直接呼叫share類名
方法即可。
✨快將這段程式碼新增到你的程式碼庫中去吧
✨本文借鑑了很多前輩的文章,如果有不對的地方請指正,歡迎大家一起交流學習 xx_cc 。