iOS ObjectC單例詳解

在開(kāi)發(fā)中經(jīng)常會(huì)用到單例設(shè)計(jì)模式,目的就是為了在程序的整個(gè)生命周期內(nèi),只會(huì)創(chuàng)建一個(gè)類(lèi)的實(shí)例對(duì)象吼具,而且只要程序不被殺死被芳,該實(shí)例對(duì)象就不會(huì)被釋放。下面我們來(lái)看看單例的概念馍悟、用途、如何創(chuàng)建剩晴,以便加深理解锣咒。

作用

在應(yīng)用這個(gè)模式時(shí),單例對(duì)象的類(lèi)必須保證只有一個(gè)實(shí)例存在赞弥。許多時(shí)候整個(gè)系統(tǒng)只需要擁有一個(gè)的全局對(duì)象毅整,這樣有利于我們協(xié)調(diào)系統(tǒng)整體的行為。比如在APP開(kāi)發(fā)中我們可能在任何地方都要使用用戶(hù)的信息绽左,那么可以在登錄的時(shí)候就把用戶(hù)信息存放在一個(gè)文件里面悼嫉,這些配置數(shù)據(jù)由一個(gè)單例對(duì)象統(tǒng)一讀取,然后服務(wù)進(jìn)程中的其他對(duì)象再通過(guò)這個(gè)單例對(duì)象獲取這些配置信息拼窥。這種方式簡(jiǎn)化了在復(fù)雜環(huán)境下的配置管理戏蔑。

有的情況下,某個(gè)類(lèi)可能只能有一個(gè)實(shí)例鲁纠。比如說(shuō)你寫(xiě)了一個(gè)類(lèi)用來(lái)播放音樂(lè)总棵,那么不管任何時(shí)候只能有一個(gè)該類(lèi)的實(shí)例來(lái)播放聲音。再比如改含,一臺(tái)計(jì)算機(jī)上可以連好幾個(gè)打印機(jī)情龄,但是這個(gè)計(jì)算機(jī)上的打印程序只能有一個(gè),這里就可以通過(guò)單例模式來(lái)避免兩個(gè)打印任務(wù)同時(shí)輸出到打印機(jī)中捍壤,即在整個(gè)的打印過(guò)程中我只有一個(gè)打印程序的實(shí)例骤视。

創(chuàng)建單例

有兩種方法來(lái)創(chuàng)建單例,下面分別介紹

1鹃觉、GCD方式創(chuàng)建單例

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;

}

上面兩種方式都可以創(chuàng)建單例,而且保證了用戶(hù)不管是通過(guò)shareInstance方法帜慢,還是alloc笼裳、copy方法得到的實(shí)例都是一樣的。

上面代碼的關(guān)鍵之處就在于如何在多線(xiàn)程情況下保證創(chuàng)建的單例還是同一個(gè)粱玲。

我們先看看在GCD情況下躬柬,如果不使用dispatch_once和同步鎖創(chuàng)建單例會(huì)出現(xiàn)什么問(wèn)題,去掉兩者后創(chuàng)建單例的代碼如下

+ (instancetype)sharedInstance

{

if (_instance == nil) {

_instance = [[self alloc] init];

}

}

假設(shè)此時(shí)有兩條線(xiàn)程:線(xiàn)程1和線(xiàn)程2抽减,都在調(diào)用shareInstance方法來(lái)創(chuàng)建單例允青,那么線(xiàn)程1運(yùn)行到if (_instance == nil)出發(fā)現(xiàn)_instance = nil,那么就會(huì)初始化一個(gè)_instance,假設(shè)此時(shí)線(xiàn)程2也運(yùn)行到if的判斷處了卵沉,此時(shí)線(xiàn)程1還沒(méi)有創(chuàng)建完成實(shí)例_instance颠锉,所以此時(shí)_instance = nil還是成立的法牲,那么線(xiàn)程2又會(huì)創(chuàng)建一個(gè)_instace。

此時(shí)就創(chuàng)建了兩個(gè)實(shí)例對(duì)象琼掠,導(dǎo)致問(wèn)題拒垃。

解決辦法1、使用dispatch_once

dispatch_once保證程序在運(yùn)行過(guò)程中只會(huì)被運(yùn)行一次瓷蛙,那么假設(shè)此時(shí)線(xiàn)程1先執(zhí)行shareInstance方法悼瓮,創(chuàng)建了一個(gè)實(shí)例對(duì)象,線(xiàn)程2就不會(huì)再去執(zhí)行dispatch_once的代碼了艰猬。從而保證了只會(huì)創(chuàng)建一個(gè)實(shí)例對(duì)象横堡。

解決辦法2、使用互斥鎖

假設(shè)此時(shí)線(xiàn)程1在執(zhí)行shareInstance方法冠桃,那么synchronize大括號(hào)內(nèi)創(chuàng)建單例的代碼命贴,如下所示:

if (_instance == nil) {

_instance = [[self alloc] init];

}

就會(huì)被當(dāng)做一個(gè)任務(wù)被加上了一把鎖。此時(shí)假設(shè)線(xiàn)程2也想執(zhí)行shareInstance方法創(chuàng)建單例食听,但是看到了線(xiàn)程1加的互斥鎖胸蛛,就會(huì)進(jìn)入睡眠模式。等到線(xiàn)程1執(zhí)行完畢碳蛋,才會(huì)被喚醒胚泌,然后去執(zhí)行上面所示的創(chuàng)建單例的代碼,但是此時(shí)_instance !=nil,所以不會(huì)再創(chuàng)建新的實(shí)例對(duì)象了肃弟。從而保證只會(huì)創(chuàng)建一個(gè)實(shí)例對(duì)象玷室。

但是互斥鎖會(huì)影響性能,所以最好還是使用GCD方式創(chuàng)建單例笤受。

宏創(chuàng)建單例

如果我們需要在程序中創(chuàng)建多個(gè)單例穷缤,那么需要在每個(gè)類(lèi)中都寫(xiě)上一次上述代碼,非常繁瑣箩兽。

我們可以使用宏來(lái)封裝單例的創(chuàng)建津肛,這樣任何類(lèi)需要?jiǎng)?chuàng)建單例,只需要一行代碼就搞定了汗贫。

實(shí)現(xiàn)代碼

Singleton.h文件

==================================

#define SingletonH(name) + (instancetype)shared##name;

#define SingletonM(name) \

static id _instance; \

\

+ (instancetype)allocWithZone:(struct _NSZone *)zone \

{ \

static dispatch_once_t onceToken; \

dispatch_once(&onceToken, ^{ \

_instance = [super allocWithZone:zone]; \

}); \

return _instance; \

} \

\

+ (instancetype)shared##name \

{ \

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; \

}

如何調(diào)用

假設(shè)我們要在類(lèi)viewcontroller中使用身坐,調(diào)用方法如下:

viewcontroller.h文件

===========================

#import

#import "Singleton.h"

@interface ViewController : UIViewController

SingletonH(viewController)

@end

viewcontroller.m文件

===========================

@interface ViewController ()

@end

@implementation ViewController

SingletonM(ViewController)

- (void)viewDidLoad {

[super viewDidLoad];

NSLog(@"%@ %@ %@ %@", [ViewController sharedViewController],[ViewController sharedViewController], [[ViewController alloc] init],[[ViewController alloc] init]);

}

@end

輸出結(jié)果

QQ截圖20160612185918.png

可以看到四個(gè)對(duì)象的內(nèi)存地址完全一樣,說(shuō)明是同一個(gè)對(duì)象落包。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末部蛇,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子咐蝇,更是在濱河造成了極大的恐慌涯鲁,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異抹腿,居然都是意外死亡岛请,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)警绩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)崇败,“玉大人,你說(shuō)我怎么就攤上這事肩祥×糯遥” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵搭幻,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我逞盆,道長(zhǎng)檀蹋,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任云芦,我火速辦了婚禮俯逾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舅逸。我一直安慰自己桌肴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布琉历。 她就那樣靜靜地躺著坠七,像睡著了一般。 火紅的嫁衣襯著肌膚如雪旗笔。 梳的紋絲不亂的頭發(fā)上彪置,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音蝇恶,去河邊找鬼拳魁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛撮弧,可吹牛的內(nèi)容都是我干的潘懊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼贿衍,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼授舟!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起舌厨,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤岂却,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體躏哩,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡署浩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扫尺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筋栋。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖正驻,靈堂內(nèi)的尸體忽然破棺而出弊攘,到底是詐尸還是另有隱情,我是刑警寧澤姑曙,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布襟交,位于F島的核電站,受9級(jí)特大地震影響伤靠,放射性物質(zhì)發(fā)生泄漏捣域。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一宴合、第九天 我趴在偏房一處隱蔽的房頂上張望焕梅。 院中可真熱鬧,春花似錦卦洽、人聲如沸贞言。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)该窗。三九已至,卻和暖如春蚤霞,著一層夾襖步出監(jiān)牢的瞬間挪捕,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工争便, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留级零,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓滞乙,卻偏偏與公主長(zhǎng)得像奏纪,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子斩启,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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

  • 在開(kāi)發(fā)中經(jīng)常會(huì)用到單例設(shè)計(jì)模式序调,目的就是為了在程序的整個(gè)生命周期內(nèi),只會(huì)創(chuàng)建一個(gè)類(lèi)的實(shí)例對(duì)象兔簇,而且只要程序不被殺死...
    不要重名就好閱讀 543評(píng)論 0 0
  • 在開(kāi)發(fā)中經(jīng)常會(huì)用到單例設(shè)計(jì)模式发绢,目的就是為了在程序的整個(gè)生命周期內(nèi)硬耍,只會(huì)創(chuàng)建一個(gè)類(lèi)的實(shí)例對(duì)象,而且只要程序不被殺死...
    零度_不結(jié)冰閱讀 441評(píng)論 0 0
  • 原文地址 http://www.cocoachina.com/ios/20160907/17497.html 在開(kāi)...
    Amok校長(zhǎng)閱讀 458評(píng)論 0 0
  • 進(jìn)程和線(xiàn)程 首先边酒,在了解多線(xiàn)程之前要了解什么是進(jìn)程经柴,什么是線(xiàn)程 什么是進(jìn)程呢?進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程...
    擱淺的青蛙閱讀 360評(píng)論 0 0
  • 大概一個(gè)多月以前墩朦,我寫(xiě)過(guò)一篇《人生就是一場(chǎng)盛大的主題閱讀》坯认,在寫(xiě)那篇文章的時(shí)候,我就發(fā)現(xiàn)這個(gè)話(huà)題實(shí)在是太深太廣氓涣,不...
    零瓏心閱讀 713評(píng)論 0 0