在開發(fā)中經(jīng)常會用到單例設(shè)計模式卢肃,目的就是為了在程序的整個生命周期內(nèi)陶珠,只會創(chuàng)建一個類的實例對象,而且只要程序不被殺死徐裸,該實例對象就不會被釋放
在應(yīng)用這個模式時船万,單例對象的類必須保證只有一個實例存在刻撒。許多時候整個系統(tǒng)只需要擁有一個的全局對象,這樣有利于我們協(xié)調(diào)系統(tǒng)整體的行為耿导。比如在APP開發(fā)中我們可能在任何地方都要使用用戶的信息声怔,那么可以在登錄的時候就把用戶信息存放在一個文件里面,這些配置數(shù)據(jù)由一個單例對象統(tǒng)一讀取舱呻,然后服務(wù)進程中的其他對象再通過這個單例對象獲取這些配置信息醋火。這種方式簡化了在復(fù)雜環(huán)境下的配置管理。
創(chuàng)建方式
1.GCD
static id _instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
+ (instancetype)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
- (id)copyWithZone:(NSZone *)zone
{
return _instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
return _instance;
}
2.互斥鎖方式
static id _instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{ @synchronized(self) { if (_instance == nil) {
_instance = [super allocWithZone:zone];
}
} return _instance;
}
+ (instancetype)sharedInstance
{ @synchronized(self) { if (_instance == nil) {
_instance = [[self alloc] init];
}
} return _instance;
}
- (id)copyWithZone:(NSZone *)zone
{ return _instance;
}
假設(shè)此時有兩條線程:線程1和線程2箱吕,都在調(diào)用shareInstance方法來創(chuàng)建單例芥驳,那么線程1運行到if (_instance == nil)出發(fā)現(xiàn)_instance = nil,那么就會初始化一個_instance,假設(shè)此時線程2也運行到if的判斷處了茬高,此時線程1還沒有創(chuàng)建完成實例_instance兆旬,所以此時_instance = nil還是成立的,那么線程2又會創(chuàng)建一個_instace怎栽。
此時就創(chuàng)建了兩個實例對象丽猬,導(dǎo)致問題宿饱。
解決辦法1、使用dispatch_once
dispatch_once保證程序在運行過程中只會被運行一次宝鼓,那么假設(shè)此時線程1先執(zhí)行shareInstance方法刑棵,創(chuàng)建了一個實例對象,線程2就不會再去執(zhí)行dispatch_once的代碼了愚铡。從而保證了只會創(chuàng)建一個實例對象蛉签。
解決辦法2、使用互斥鎖
假設(shè)此時線程1在執(zhí)行shareInstance方法沥寥,那么synchronize大括號內(nèi)創(chuàng)建單例的代碼碍舍,如下所示:
if (_instance == nil) {
_instance = [[self alloc] init];
}
就會被當(dāng)做一個任務(wù)被加上了一把鎖。此時假設(shè)線程2也想執(zhí)行shareInstance方法創(chuàng)建單例邑雅,但是看到了線程1加的互斥鎖片橡,就會進入睡眠模式。等到線程1執(zhí)行完畢淮野,才會被喚醒捧书,然后去執(zhí)行上面所示的創(chuàng)建單例的代碼,但是此時_instance !=nil,所以不會再創(chuàng)建新的實例對象了骤星。從而保證只會創(chuàng)建一個實例對象经瓷。
但是互斥鎖會影響性能,所以最好還是使用GCD方式創(chuàng)建單例洞难。
1 重寫+allocWithZone方法舆吮,保證永遠都只為單例對象分配一次內(nèi)存空間
因為每調(diào)用一次這個方法,就會重新分配一個新的內(nèi)存空間队贱。
2嚴謹起見色冀,重寫-copyWithZone方法和-MutableCopyWithZone方法
為了防止通過copy來創(chuàng)建新的實例我們要重寫copyWithZone MutableCopyWithZone
單例的優(yōu)點:
1.一個類只被實例化一次,提供了對唯一實例的受控訪問
2.節(jié)省系統(tǒng)資源
缺點:
1.一個類只有一個對象柱嫌,可能造成責(zé)任過重锋恬,在一定程度上違背了“單一職責(zé)原則”
2.由于單利模式中沒有抽象層,因此單例類的擴展有很大的困難编丘。
3.濫用單例將帶來一些負面問題与学,如為了節(jié)省資源將數(shù)據(jù)庫連接池對象設(shè)計為的單例類,可能會導(dǎo)致共享連接池對象的程序過多而出現(xiàn)連接池溢出瘪吏;如果實例化的對象長時間不被利用,系統(tǒng)會認為是垃圾而被回收蜗巧,這將導(dǎo)致對象狀態(tài)的丟失掌眠。