單例
概念:整個(gè)應(yīng)用或系統(tǒng)只能有該類的一個(gè)實(shí)例,即是在整個(gè)項(xiàng)目中,這個(gè)類的對(duì)象只能被初始化一次愚隧。單例類保證了應(yīng)用程序的生命周期中有且僅有一個(gè)該類的實(shí)例對(duì)象,而且易于外界訪問锻全。
特點(diǎn)
內(nèi)存占用與運(yùn)行時(shí)間
對(duì)比使用單例模式和非單例模式的例子狂塘,在內(nèi)存占用與運(yùn)行時(shí)間存在以下差距:
(1) 單例模式:?jiǎn)卫J矫看潍@取實(shí)例時(shí)都會(huì)先進(jìn)行判斷录煤,看該實(shí)例是否存在——如果存在,則返回荞胡;否則妈踊,則創(chuàng)建實(shí)例。因此泪漂,會(huì)浪費(fèi)一些判斷的時(shí)間廊营。但是,如果一直沒有人使用這個(gè)實(shí)例的話萝勤,那么就不會(huì)創(chuàng)建實(shí)例赘风,節(jié)約了內(nèi)存空間。
(2) 非單例模式:當(dāng)類加載的時(shí)候就會(huì)創(chuàng)建類的實(shí)例纵刘,不管你是否使用它邀窃。然后當(dāng)每次調(diào)用的時(shí)候就不需要判斷該實(shí)例是否存在了,節(jié)省了運(yùn)行的時(shí)間假哎。但是如果該實(shí)例沒有使用的話瞬捕,就浪費(fèi)了內(nèi)存。
線程的安全性
(1) 從線程的安全性上來講舵抹,不加同步的單例模式是不安全的肪虎。比如,有兩個(gè)線程惧蛹,一個(gè)是線程A扇救,另外一個(gè)是線程B,如果它們同時(shí)調(diào)用某一個(gè)方法香嗓,那就可能會(huì)導(dǎo)致并發(fā)問題迅腔。在這種情況下,會(huì)創(chuàng)建出兩個(gè)實(shí)例來靠娱,也就是單例的控制在并發(fā)情況下失效了沧烈。
(2) 非單例模式是線程安全的,因?yàn)槌绦虮WC只加載一次像云,在加載的時(shí)候不會(huì)發(fā)生并發(fā)情況锌雀。
(3) 單例模式如果要實(shí)現(xiàn)線程安全,只需要加上synchronized即可迅诬。但是這樣一來腋逆,就會(huì)減低整個(gè)程序的訪問速度,而且每次都要判斷侈贷,比較麻煩惩歉。
(4) 雙重檢查加鎖:為了解決(3)的繁瑣問題,可以使用“雙重檢查加鎖”的方式來實(shí)現(xiàn),這樣柬泽,就可以既實(shí)現(xiàn)線程安全,又能使得程序性能不受太大的影響嫁蛇。
單例模式會(huì)阻止其它對(duì)象實(shí)例化其自己的對(duì)象的副本锨并,從而確保所有對(duì)象都訪問唯一實(shí)例。
因?yàn)閱卫J降念惪刂屏藢?shí)例化的過程睬棚,所以類可以更加靈活修改實(shí)例化過程第煮。
基本步驟
(1) 為單例對(duì)象創(chuàng)建一個(gè)靜態(tài)實(shí)例,可以寫成全局的抑党,也可以在類方法里面實(shí)現(xiàn)包警,并初始化為nil;
(2) 實(shí)現(xiàn)一個(gè)實(shí)例構(gòu)造方法底靠,檢查上面聲明的靜態(tài)實(shí)例是否為nil害晦,如果是,則創(chuàng)建并返回一個(gè)本類的實(shí)例暑中;
(3) 重寫allocWithZone方法壹瘟,用來保證其他人直接使用alloc和init試圖獲得一個(gè)新實(shí)力的時(shí)候不產(chǎn)生一個(gè)新實(shí)例;
(4) 適當(dāng)實(shí)現(xiàn)allocWitheZone鳄逾,copyWithZone稻轨,release和autorelease。
static ZYSingleton* _instance = nil;
+(instancetype)shareInstance
{
///加上GCD代碼是為了防止多個(gè)線程同時(shí)訪問這個(gè)類雕凹,從而造成產(chǎn)生多個(gè)實(shí)例
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[super allocWithZone:NULL] init];
});
return _instance;
}
+(id)allocWithZone:(struct _NSZone *)zone
{
return [ZYSingleton shareInstance];
}
-(id) copyWithZone:(struct _NSZone *)zone
{
return [ZYSingleton shareInstance];
}
注:GCD的代碼也可以使用以下部分代替殴俱,但以下的代碼性能不好:
?@synchronized (self) {
? ? ? ?// 為了防止多線程同時(shí)訪問對(duì)象,造成多次分配內(nèi)存空間枚抵,所以要加上線程鎖
? ? ? ?if (_instance == nil) {
? ? ? ? ? ? _instance = [super allocWithZone:zone];
? ? ? ?}
? ? ? ? return _instance;
? ? }