IOS單例模式的底層原理

單例介紹

本文源碼下載地址

1.什么是單例

說到單例首先要提到單例模式,因?yàn)閱卫J绞菃卫嬖诘哪康?/p>

單例模式是一種常用的軟件設(shè)計(jì)模式。在它的核心結(jié)構(gòu)中只包含一個(gè)被稱為單例類的特殊類。通過單例模式可以保證系統(tǒng)中一個(gè)類只有一個(gè)實(shí)例而且該實(shí)例易于外界訪問,從而方便對(duì)實(shí)例個(gè)數(shù)的控制并節(jié)約系統(tǒng)資源。如果希望在系統(tǒng)中某個(gè)類的對(duì)象只能存在一個(gè),單例模式是最好的解決方案。

單例,顧名思義:單獨(dú)的實(shí)例。

簡單的說,單例是一個(gè)特殊的實(shí)例,在單例所屬的類中只存在單例這么一個(gè)實(shí)例,并且單例類似全局變量,在系統(tǒng)任意地方都能訪問單例

2.單例用處

根據(jù)單例模式的定義,我們知道一般兩種情況下使用單例:

系統(tǒng)中某種對(duì)象只能存在一個(gè),多了就會(huì)出問題

系統(tǒng)中某種對(duì)象實(shí)例只需要一個(gè)就夠用了,多了占內(nèi)存

對(duì)于第一種情況,我們必須使用單例,對(duì)于第二種情況,我們雖然可以不用單例,但是單例是更優(yōu)的選擇

iOS的系統(tǒng)中有很多地方用的都是單例,例如

[UIApplication sharedApplication];
[NSNotificationCenter defaultCenter];[NSFileManager defaultManager];
[NSUserDefaults standardUserDefaults];[NSURLCache sharedURLCache];[NSHTTPCookieStorage sharedHTTPCookieStorage];

iOS單例的創(chuàng)建

1.單線程單例

我們知道對(duì)于單例類,我們必須留出一個(gè)接口來返回生成的單例,由于一個(gè)類中只能有一個(gè)實(shí)例,所以我們?cè)诘谝淮卧L問這個(gè)實(shí)例的時(shí)候創(chuàng)建,之后訪問直接取已經(jīng)創(chuàng)建好的實(shí)例

@implementationSingleton
+ (instancetype)shareInstance{
staticSingleton* single;
if(!single) {      
  single = [[Singleton alloc] init];   
 }
return single;
}
@end

ps:嚴(yán)格意義上來說,我們還需要將alloc方法封住,因?yàn)閲?yán)格的單例是不允許再創(chuàng)建其他實(shí)例的,而alloc方法可以在外部任意生成實(shí)例。但是考慮到alloc屬于NSObject,iOS中無法將alloc變成私有方法,最多只能覆蓋alloc讓其返回空,不過這樣做也可能會(huì)讓使用接口的人誤解,造成其他問題。所以我們一般情況下對(duì)alloc不做特殊處理。系統(tǒng)的單例也未對(duì)alloc做任何處理

2.@synchronized單例

對(duì)于一個(gè)實(shí)例,我們一般并不能保證他一定會(huì)在單線程模式下使用,所以我們得適配多線程情況。在多線程情況下,上面的單例創(chuàng)建方式可能會(huì)出現(xiàn)問題。如果兩個(gè)線程同時(shí)調(diào)用shareInstance,可能會(huì)創(chuàng)建出2個(gè)single來。所以對(duì)于多線程情況下,我們需要使用@synchronized來加鎖。

@implementationSingleton
+ (instancetype)shareInstance{
staticSingleton* single;
@synchronized(self){
if(!single) {           
 single = [[Singleton alloc] init];       
 }    
}
return single;
}
@end

這樣的話,當(dāng)多個(gè)線程同時(shí)調(diào)用shareInstance時(shí),由于@synchronized已經(jīng)加鎖,所以只能有一個(gè)線程進(jìn)入創(chuàng)建single。這樣就解決了多線程下調(diào)用單例的問題

3.dispatch_once單例

使用@synchronized雖然解決了多線程的問題,但是并不完美。因?yàn)橹挥性趕ingle未創(chuàng)建時(shí),我們加鎖才是有必要的。如果single已經(jīng)創(chuàng)建.這時(shí)候鎖不僅沒有好處,而且還會(huì)影響到程序執(zhí)行的性能(多個(gè)線程執(zhí)行@synchronized中的代碼時(shí),只有一個(gè)線程執(zhí)行,其他線程需要等待)。那么有沒有方法既可以解決問題,又不影響性能呢?
這個(gè)方法就是GCD中的dispatch_once

@implementationSingleton
+ (instancetype)shareInstance{
    static Singleton* single;
    static dispatch_once_t onceToken;
    //①onceToken = 0;
    
    dispatch_once(&onceToken, ^{
        
        NSLog(@"%ld",onceToken);
        //②onceToken = 140734731430192
        single = [[Singleton alloc] init];
    });
    
    NSLog(@"%ld",onceToken);
    //③onceToken = -1;
    return single;
}
}
@end

打印結(jié)果如下:

2016-12-19 17:39:28.484 11-一次性執(zhí)行[9619:621917] 140734605830464
2016-12-19 17:39:28.484 11-一次性執(zhí)行[9619:621917] -1

dispatch_once為什么能做到既解決同步多線程問題又不影響性能呢?
下面我們來看看dispatch_once的原理:
dispatch_once主要是根據(jù)onceToken的值來決定怎么去執(zhí)行代碼。
當(dāng)onceToken= 0時(shí),線程執(zhí)行dispatch_once的block中代碼
當(dāng)onceToken= -1時(shí),線程跳過dispatch_once的block中代碼不執(zhí)行
當(dāng)onceToken為其他值時(shí),線程被線程被阻塞,等待onceToken值改變
當(dāng)線程首先調(diào)用shareInstance,某一線程要執(zhí)行block中的代碼時(shí),首先需要改變onceToken的值,再去執(zhí)行block中的代碼。這里onceToken的值變?yōu)榱?40734605830464。
這樣當(dāng)其他線程再獲取onceToken的值時(shí),值已經(jīng)變?yōu)?40734605830464。其他線程被阻塞。
當(dāng)block線程執(zhí)行完block之后。onceToken變?yōu)?1。其他線程不再阻塞,跳過block。
下次再調(diào)用shareInstance時(shí),block已經(jīng)為-1。直接跳過block。
這樣dispatch_once在首次調(diào)用時(shí)同步阻塞線程,生成單例之后,不再阻塞線程。dispatch_once是創(chuàng)建單例的最優(yōu)方案
總結(jié):
單例模式是一個(gè)很好的設(shè)計(jì)模式,他就像一個(gè)全局變量一樣,可以讓我們?cè)谌魏蔚胤蕉际褂猛粋€(gè)實(shí)例。
如果要自己創(chuàng)建單例模式,最好使用dispatch_once方法,這樣即可解決多線程問題,又能達(dá)到高效的目的

單例雖然好用,不過他并不適合繼承和擴(kuò)展,所以使用單例的時(shí)候要注意這點(diǎn)。千萬不要任何東西都使用單例,要適可而止

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,716評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評(píng)論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,706評(píng)論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,036評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評(píng)論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,203評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,725評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,451評(píng)論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,677評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評(píng)論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,857評(píng)論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評(píng)論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,407評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,643評(píng)論 2 380

推薦閱讀更多精彩內(nèi)容

  • 單例模式 什么是單例模式? 單例模式想一個(gè)大獨(dú)裁者,他規(guī)定在他的國度里面,所有數(shù)據(jù)的訪問和請(qǐng)求都得經(jīng)過他,甚至你要...
    GitHubPorter閱讀 1,177評(píng)論 0 4
  • 經(jīng)常用到單例,但也僅僅是停留在會(huì)用的層次,至于為什么這么用,內(nèi)部怎么實(shí)現(xiàn)的,從未研究過。在面試的時(shí)候,被問到了單例...
    YSL一路行走閱讀 2,610評(píng)論 5 29
  • coding 的演示功能不讓用,原來搭建的博客訪問不了了。索性將全部博客遷移到簡書,這篇是舊文章,歡迎大家以后來簡...
    小笨狼閱讀 859評(píng)論 0 14
  • 概要 單例模式是常見的設(shè)計(jì)模式。它的核心結(jié)構(gòu)中只包含一個(gè)被稱為單例類的特殊類。 通過單例模式可以保證系統(tǒng)中單例類只...
    NapoleonY閱讀 268評(píng)論 0 1
  • 一、李嘉誠戴的表,是西鐵城表。市價(jià)1000港元,他已戴了十幾年。他戴的眼鏡,也用了十幾年了,曾因度數(shù)增加換過鏡片,...
    蔣昊軒閱讀 571評(píng)論 0 1