一. 單例模式簡介
- 單例模式的作用
可以保證在程序運行過程,一個類只有一個實例,而且該實例易于供外界訪問
從而方便地控制了實例個數,并節約系統資源 - 單例模式的使用場合
在整個應用程序中,共享一份資源(這份資源只需要創建初始化1次),一般用于工具類。例如:登陸控制器,網絡數據請求,音樂播放器等一個工程需要使用多次的控制器或方法。 - 單例模式的優缺點
- 優點:
1.單例模式可以保證系統中一個類只有一個實例而且該實例易于外界訪問,從而方便對實例個數的控制并節約系統資源。
2.如果希望在系統中某個類的對象只能存在一個,單例模式是最好的解決方案。
3.單例模式因為類控制了實例化過程,所以類可以更加靈活修改實例化過程。 - 缺點:
1.單例對象一旦建立,對象指針是保存在靜態區的,單例對象在堆中分配的內存空間,會在應用程序終止后才會被釋放。
2.單例類無法繼承,因此很難進行類的擴展。
3.單例不適用于變化的對象,如果同一類型的對象總是要在不同的用例場景發生變化,單例就會引起數據的錯誤,不能保存彼此的狀態。
注意:我們在使用單例類之前,一定要考慮好單例類是否適合和類以后的擴展性,避免盲目濫用單例
二. 單例在ARC中的實現
ARC中單例實現步驟
1 在類的內部提供一個static修飾的全局變量
2 提供一個類方法,方便外界訪問
3 重寫+allocWithZone方法,保證永遠都只為單例對象分配一次內存空間
4 嚴謹起見,重寫-copyWithZone方法和-MutableCopyWithZone方法
ARC中單例代碼實現
#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代碼基礎上重寫下面的三個方法即可
-(oneway void)release
{
}
-(instancetype)retain
{
return _instance;
}
-(NSUInteger)retainCount
{
return MAXFLOAT;
}
四. 一勞永逸,單例模式的優化
如果想要一勞永逸,我們將面臨兩個問題
1:如何寫一份單例代碼在ARC和MRC環境下都適用?
2:如何使一份單例代碼可以多個類共同使用
為了解決這兩個問題,我們可以在PCH文件使用代參數的宏和條件編譯
利用條件編譯來判斷是ARC還是MRC環境
#if __has_feature(objc_arc)
//如果是ARC,那么就執行這里的代碼1
#else
//如果不是ARC,那么就執行代理的代碼2
#endif
注意:單例模式不可以使用繼承,因為使用繼承,同時也會繼承靜態變量,當子類和父類同時創建的時候只會創建一個先創建的實例對象。
廢話不多說了直接上代碼
PCH文件Single.h
#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類名方法即可。