單例模式-Singleton Pattern是 Java 中最簡單的設(shè)計(jì)模式之一芹壕。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式拉背,它提供了一種創(chuàng)建對象的最佳方式断箫」辖【學(xué)習(xí)難度:★☆☆☆☆,使用頻率:★★★★☆】
確保對象的唯一性——單例模式 (一):單例模式的動(dòng)機(jī)比原,單例模式概述
確保對象的唯一性——單例模式 (二):負(fù)載均衡器的設(shè)計(jì)與實(shí)現(xiàn)
確保對象的唯一性——單例模式 (三):餓漢式單例與懶漢式單例的討論
確保對象的唯一性——單例模式 (四):一種更好的單例實(shí)現(xiàn)方法(靜態(tài)內(nèi)部類)
確保對象的唯一性——單例模式 (五):單例模式總結(jié)
這種模式涉及到一個(gè)單一的類插佛,該類負(fù)責(zé)創(chuàng)建自己的對象,同時(shí)確保只有單個(gè)對象被創(chuàng)建量窘。這個(gè)類提供了一種訪問其唯一的對象的方式雇寇,可以直接訪問,不需要實(shí)例化該類的對象蚌铜。
注意:
1锨侯、單例類只能有一個(gè)實(shí)例。
2冬殃、單例類必須自己創(chuàng)建自己的唯一實(shí)例囚痴。
3、單例類必須給所有其他對象提供這一實(shí)例审葬。
介紹
意圖:保證一個(gè)類僅有一個(gè)實(shí)例深滚,并提供一個(gè)訪問它的全局訪問點(diǎn)。
主要解決:一個(gè)全局使用的類頻繁地創(chuàng)建與銷毀耳璧。
何時(shí)使用:當(dāng)您想控制實(shí)例數(shù)目成箫,節(jié)省系統(tǒng)資源的時(shí)候。
如何解決:判斷系統(tǒng)是否已經(jīng)有這個(gè)單例旨枯,如果有則返回蹬昌,如果沒有則創(chuàng)建。
關(guān)鍵代碼:構(gòu)造函數(shù)是私有的攀隔。
應(yīng)用實(shí)例:
1皂贩、一個(gè)黨只能有一個(gè)主席。
2昆汹、Windows 是多進(jìn)程多線程的明刷,在操作一個(gè)文件的時(shí)候,就不可避免地出現(xiàn)多個(gè)進(jìn)程或線程同時(shí)操作一個(gè)文件的現(xiàn)象满粗,所以所有文件的處理必須通過唯一的實(shí)例來進(jìn)行辈末。
3、一些設(shè)備管理器常常設(shè)計(jì)為單例模式映皆,比如一個(gè)電腦有兩臺打印機(jī)挤聘,在輸出的時(shí)候就要處理不能兩臺打印機(jī)打印同一個(gè)文件。
優(yōu)點(diǎn):
1捅彻、在內(nèi)存里只有一個(gè)實(shí)例组去,減少了內(nèi)存的開銷,尤其是頻繁的創(chuàng)建和銷毀實(shí)例(比如管理學(xué)院首頁頁面緩存)步淹。
2从隆、避免對資源的多重占用(比如寫文件操作)诚撵。
缺點(diǎn):
沒有接口,不能繼承键闺,與單一職責(zé)原則沖突寿烟,一個(gè)類應(yīng)該只關(guān)心內(nèi)部邏輯,而不關(guān)心外面怎么樣來實(shí)例化艾杏。
使用場景:
1韧衣、要求生產(chǎn)唯一序列號。
2购桑、WEB 中的計(jì)數(shù)器,不用每次刷新都在數(shù)據(jù)庫里加一次氏淑,用單例先緩存起來勃蜘。
3、創(chuàng)建的一個(gè)對象需要消耗的資源過多假残,比如 I/O 與數(shù)據(jù)庫的連接等缭贡。
注意事項(xiàng):getInstance() 方法中需要使用同步鎖 synchronized (Singleton.class) 防止多線程同時(shí)進(jìn)入造成 instance 被多次實(shí)例化。
iOS中的單例模式:
ARC中單例實(shí)現(xiàn)步驟
1 在類的內(nèi)部提供一個(gè)static修飾的全局變量
2 提供一個(gè)類方法辉懒,方便外界訪問
3 重寫+allocWithZone方法阳惹,保證永遠(yuǎn)都只為單例對象分配一次內(nèi)存空間
4 嚴(yán)謹(jǐn)起見,重寫-copyWithZone方法和-MutableCopyWithZone方法
#import "Tools.h"
@implementation Tools
// 創(chuàng)建靜態(tài)對象 防止外部訪問
static Tools *_instance;
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
// @synchronized (self) {
// // 為了防止多線程同時(shí)訪問對象眶俩,造成多次分配內(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í)例易于外界訪問 我們一般提供一個(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),也要重寫copyWithZone 和 mutableCopyWithZone
-(id)copyWithZone:(NSZone *)zone
{
return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
return _instance;
}
MRC單例實(shí)現(xiàn)步驟
1 在類的內(nèi)部提供一個(gè)static修飾的全局變量
2 提供一個(gè)類方法颠印,方便外界訪問
3 重寫+allocWithZone方法纲岭,保證永遠(yuǎn)都只為單例對象分配一次內(nèi)存空間
4 嚴(yán)謹(jǐn)起見,重寫-copyWithZone方法和-MutableCopyWithZone方法
5 重寫release方法
6 重寫retain方法
7 建議在retainCount方法中返回一個(gè)最大值
配置MRC環(huán)境
1 注意ARC不是垃圾回收機(jī)制线罕,是編譯器特性
2 配置MRC環(huán)境:build setting ->搜索automatic ref->修改為N0
-(oneway void)release
{
}
-(instancetype)retain
{
return _instance;
}
-(NSUInteger)retainCount
{
return MAXFLOAT;
}
一勞永逸止潮,單例模式的優(yōu)化
如果想要一勞永逸,我們將面臨兩個(gè)問題
1:如何寫一份單例代碼在ARC和MRC環(huán)境下都適用钞楼?
2:如何使一份單例代碼可以多個(gè)類共同使用
為了解決這兩個(gè)問題喇闸,我們可以在PCH文件使用代參數(shù)的宏和條件編譯
利用條件編譯來判斷是ARC還是MRC環(huán)境
#if __has_feature(objc_arc)
//如果是ARC,那么就執(zhí)行這里的代碼1
#else
//如果不是ARC询件,那么就執(zhí)行代理的代碼2
#endif
注意:單例模式不可以使用繼承燃乍,因?yàn)槭褂美^承,同時(shí)也會(huì)繼承靜態(tài)變量雳殊,當(dāng)子類和父類同時(shí)創(chuàng)建的時(shí)候只會(huì)創(chuàng)建一個(gè)先創(chuàng)建的實(shí)例對象橘沥。
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
用法:
在.h文件中調(diào)用singleH(類名)
在.m文件中調(diào)用singleM(類名)
創(chuàng)建類時(shí)直接調(diào)用share類名方法即可。
單例模式中夯秃,為什么用 static 修飾 dispatch_once_t座咆?
比如:
+ (instancetype)sharedManager
{
static PhotoManager *sharedPhotoManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[NSThread sleepForTimeInterval:2];
sharedPhotoManager = [[PhotoManager alloc] init];
NSLog(@"Singleton has memory address at: %@", sharedPhotoManager);
[NSThread sleepForTimeInterval:2];
sharedPhotoManager->_photosArray = [NSMutableArray array];
});
return sharedPhotoManager;
}
這里有:
static PhotoManager *sharedPhotoManager = nil;
static dispatch_once_t onceToken;
為何用static修飾痢艺?不用staitc有何影響?
答:靜態(tài)局部變量介陶,雖然作用域沒變堤舒,但是可以整個(gè)程序生命周期都保持不被銷毀。
參考文章:
iOS-單例模式寫一次就夠了