IOS單例模式的底層原理

單例介紹

本文源碼下載地址

1.什么是單例

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

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

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

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

2.單例用處

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

系統(tǒng)中某種對象只能存在一個友酱,多了就會出問題

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

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

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

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

iOS單例的創(chuàng)建

1.單線程單例

我們知道對于單例類或详,我們必須留出一個接口來返回生成的單例系羞,由于一個類中只能有一個實(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讓其返回空夹供,不過這樣做也可能會讓使用接口的人誤解灵份,造成其他問題。所以我們一般情況下對alloc不做特殊處理哮洽。系統(tǒng)的單例也未對alloc做任何處理

2.@synchronized單例

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

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

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

3.dispatch_once單例

使用@synchronized雖然解決了多線程的問題,但是并不完美隐轩。因?yàn)橹挥性趕ingle未創(chuàng)建時饺饭,我們加鎖才是有必要的。如果single已經(jīng)創(chuàng)建.這時候鎖不僅沒有好處职车,而且還會影響到程序執(zhí)行的性能(多個線程執(zhí)行@synchronized中的代碼時瘫俊,只有一個線程執(zhí)行,其他線程需要等待)悴灵。那么有沒有方法既可以解決問題扛芽,又不影響性能呢?
這個方法就是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時,線程執(zhí)行dispatch_once的block中代碼
當(dāng)onceToken= -1時茫孔,線程跳過dispatch_once的block中代碼不執(zhí)行
當(dāng)onceToken為其他值時叮喳,線程被線程被阻塞被芳,等待onceToken值改變
當(dāng)線程首先調(diào)用shareInstance,某一線程要執(zhí)行block中的代碼時馍悟,首先需要改變onceToken的值畔濒,再去執(zhí)行block中的代碼。這里onceToken的值變?yōu)榱?40734605830464赋朦。
這樣當(dāng)其他線程再獲取onceToken的值時篓冲,值已經(jīng)變?yōu)?40734605830464。其他線程被阻塞宠哄。
當(dāng)block線程執(zhí)行完block之后壹将。onceToken變?yōu)?1。其他線程不再阻塞毛嫉,跳過block诽俯。
下次再調(diào)用shareInstance時,block已經(jīng)為-1承粤。直接跳過block暴区。
這樣dispatch_once在首次調(diào)用時同步阻塞線程,生成單例之后辛臊,不再阻塞線程仙粱。dispatch_once是創(chuàng)建單例的最優(yōu)方案
總結(jié):
單例模式是一個很好的設(shè)計(jì)模式,他就像一個全局變量一樣彻舰,可以讓我們在任何地方都使用同一個實(shí)例伐割。
如果要自己創(chuàng)建單例模式,最好使用dispatch_once方法刃唤,這樣即可解決多線程問題隔心,又能達(dá)到高效的目的

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末笼裳,一起剝皮案震驚了整個濱河市唯卖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌躬柬,老刑警劉巖耐床,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異楔脯,居然都是意外死亡撩轰,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來堪嫂,“玉大人偎箫,你說我怎么就攤上這事〗源” “怎么了淹办?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長恶复。 經(jīng)常有香客問我怜森,道長,這世上最難降的妖魔是什么谤牡? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任副硅,我火速辦了婚禮,結(jié)果婚禮上翅萤,老公的妹妹穿的比我還像新娘恐疲。我一直安慰自己,他們只是感情好套么,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布培己。 她就那樣靜靜地躺著,像睡著了一般胚泌。 火紅的嫁衣襯著肌膚如雪省咨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天玷室,我揣著相機(jī)與錄音零蓉,去河邊找鬼。 笑死阵苇,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的感论。 我是一名探鬼主播绅项,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼比肄!你這毒婦竟也來了快耿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤芳绩,失蹤者是張志新(化名)和其女友劉穎掀亥,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妥色,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搪花,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撮竿。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡吮便,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出幢踏,到底是詐尸還是另有隱情髓需,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布房蝉,位于F島的核電站僚匆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏搭幻。R本人自食惡果不足惜咧擂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望粗卜。 院中可真熱鬧屋确,春花似錦、人聲如沸续扔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纱昧。三九已至刨啸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間识脆,已是汗流浹背设联。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留灼捂,地道東北人离例。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像悉稠,于是被迫代替她去往敵國和親宫蛆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

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

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