一. 單例模式簡(jiǎn)介
單例模式的作用
可以保證在程序運(yùn)行過(guò)程摩桶,一個(gè)類只有一個(gè)實(shí)例肮韧,而且該實(shí)例易于供外界訪問(wèn)
從而方便地控制了實(shí)例個(gè)數(shù)谓形,并節(jié)約系統(tǒng)資源單例模式的使用場(chǎng)合
在整個(gè)應(yīng)用程序中拟烫,共享一份資源(這份資源只需要?jiǎng)?chuàng)建初始化1次)宣蔚,一般用于工具類向抢。例如:登陸控制器,網(wǎng)絡(luò)數(shù)據(jù)請(qǐng)求胚委,音樂(lè)播放器等一個(gè)工程需要使用多次的控制器或方法挟鸠。單例模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
單例模式可以保證系統(tǒng)中一個(gè)類只有一個(gè)實(shí)例而且該實(shí)例易于外界訪問(wèn),從而方便對(duì)實(shí)例個(gè)數(shù)的控制并節(jié)約系統(tǒng)資源亩冬。
如果希望在系統(tǒng)中某個(gè)類的對(duì)象只能存在一個(gè)艘希,單例模式是最好的解決方案。
單例模式因?yàn)轭惪刂屏藢?shí)例化過(guò)程硅急,所以類可以更加靈活修改實(shí)例化過(guò)程覆享。
缺點(diǎn):
單例對(duì)象一旦建立,對(duì)象指針是保存在靜態(tài)區(qū)的营袜,單例對(duì)象在堆中分配的內(nèi)存空間撒顿,會(huì)在應(yīng)用程序終止后才會(huì)被釋放。
單例類無(wú)法繼承荚板,因此很難進(jìn)行類的擴(kuò)展凤壁。
單例不適用于變化的對(duì)象,如果同一類型的對(duì)象總是要在不同的用例場(chǎng)景發(fā)生變化啸驯,單例就會(huì)引起數(shù)據(jù)的錯(cuò)誤客扎,不能保存彼此的狀態(tài)。
注意:我們?cè)谑褂脝卫愔胺6罚欢ㄒ紤]好單例類是否適合和類以后的擴(kuò)展性,避免盲目濫用單例
二. 單例在ARC中的實(shí)現(xiàn)
ARC中單例實(shí)現(xiàn)步驟
1 在類的內(nèi)部提供一個(gè)static修飾的全局變量
2 提供一個(gè)類方法宅楞,方便外界訪問(wèn)
3 重寫(xiě)+allocWithZone方法针姿,保證永遠(yuǎn)都只為單例對(duì)象分配一次內(nèi)存空間
4 嚴(yán)謹(jǐn)起見(jiàn)袱吆,重寫(xiě)-copyWithZone方法和-MutableCopyWithZone方法
ARC中單例代碼實(shí)現(xiàn)
#import "Tools.h"
@implementation Tools
// 創(chuàng)建靜態(tài)對(duì)象 防止外部訪問(wèn)
static Tools *_instance;
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
// @synchronized (self) {
// // 為了防止多線程同時(shí)訪問(wèn)對(duì)象,造成多次分配內(nèi)存空間距淫,所以要加上線程鎖
// 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;
}
// 為了使實(shí)例易于外界訪問(wèn) 我們一般提供一個(gè)類方法
// 類方法命名規(guī)范 share類名|default類名|類名
+(instancetype)shareTools
{
//return _instance;
// 最好用self 用Tools他的子類調(diào)用時(shí)會(huì)出現(xiàn)錯(cuò)誤
return [[self alloc]init];
}
// 為了嚴(yán)謹(jǐn)绞绒,也要重寫(xiě)copyWithZone 和 mutableCopyWithZone
-(id)copyWithZone:(NSZone *)zone
{
return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
return _instance;
}
三. 單例在MRC中的實(shí)現(xiàn)
MRC單例實(shí)現(xiàn)步驟
1 在類的內(nèi)部提供一個(gè)static修飾的全局變量
2 提供一個(gè)類方法,方便外界訪問(wèn)
3 重寫(xiě)+allocWithZone方法榕暇,保證永遠(yuǎn)都只為單例對(duì)象分配一次內(nèi)存空間
4 嚴(yán)謹(jǐn)起見(jiàn)蓬衡,重寫(xiě)-copyWithZone方法和-MutableCopyWithZone方法
5 重寫(xiě)release方法
6 重寫(xiě)retain方法
7 建議在retainCount方法中返回一個(gè)最大值
配置MRC環(huán)境
1 注意ARC不是垃圾回收機(jī)制,是編譯器特性
2 配置MRC環(huán)境:build setting ->搜索automatic ref->修改為N0
MRC中單例代碼實(shí)現(xiàn)
配置好MRC環(huán)境之后彤枢,在ARC代碼基礎(chǔ)上重寫(xiě)下面的三個(gè)方法即可
-(oneway void)release
{
}
-(instancetype)retain
{
return _instance;
}
-(NSUInteger)retainCount
{
return MAXFLOAT;
}
四. 一勞永逸狰晚,單例模式的優(yōu)化
如果想要一勞永逸,我們將面臨兩個(gè)問(wèn)題
1:如何寫(xiě)一份單例代碼在ARC和MRC環(huán)境下都適用缴啡?
2:如何使一份單例代碼可以多個(gè)類共同使用
為了解決這兩個(gè)問(wèn)題壁晒,我們可以在PCH文件使用代參數(shù)的宏和條件編譯
利用條件編譯來(lái)判斷是ARC還是MRC環(huán)境
#if __has_feature(objc_arc)
//如果是ARC,那么就執(zhí)行這里的代碼1
#else
//如果不是ARC业栅,那么就執(zhí)行代理的代碼2
#endif
注意:?jiǎn)卫J讲豢梢允褂美^承秒咐,因?yàn)槭褂美^承,同時(shí)也會(huì)繼承靜態(tài)變量碘裕,當(dāng)子類和父類同時(shí)創(chuàng)建的時(shí)候只會(huì)創(chuàng)建一個(gè)先創(chuàng)建的實(shí)例對(duì)象携取。
廢話不多說(shuō)了直接上代碼
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
這時(shí)我們就可以一勞永逸,任何項(xiàng)目中帮孔,當(dāng)我們要使用單例類的時(shí)候只要在項(xiàng)目中導(dǎo)入PCH文件然后
在.h文件中調(diào)用singleH(類名)
在.m文件中調(diào)用singleM(類名)
創(chuàng)建類時(shí)直接調(diào)用share類名
方法即可雷滋。
?快將這段代碼添加到你的代碼庫(kù)中去吧
?本文借鑒了很多前輩的文章,如果有不對(duì)的地方請(qǐng)指正你弦,歡迎大家一起交流學(xué)習(xí) xx_cc 惊豺。