iOS 關(guān)于單例的那些事

在開發(fā)中經(jīng)常會用到單例設計模式轿钠,目的就是為了在程序的整個生命周期內(nèi)沟优,只會創(chuàng)建一個類的實例對象涕滋,而且只要程序不被殺死,該實例對象就不會被釋放挠阁。下面我們來看看單例的概念宾肺、用途、如何創(chuàng)建侵俗,以便加深理解锨用。

作用

在應用這個模式時,單例對象的類必須保證只有一個實例存在隘谣。許多時候整個系統(tǒng)只需要擁有一個的全局對象增拥,這樣有利于我們協(xié)調(diào)系統(tǒng)整體的行為。比如在APP開發(fā)中我們可能在任何地方都要使用用戶的信息,那么可以在登錄的時候就把用戶信息存放在一個文件里面跪者,這些配置數(shù)據(jù)由一個單例對象統(tǒng)一讀取,然后服務進程中的其他對象再通過這個單例對象獲取這些配置信息熄求。這種方式簡化了在復雜環(huán)境下的配置管理渣玲。

有的情況下,某個類可能只能有一個實例弟晚。比如說你寫了一個類用來播放音樂忘衍,那么不管任何時候只能有一個該類的實例來播放聲音。再比如卿城,一臺計算機上可以連好幾個打印機枚钓,但是這個計算機上的打印程序只能有一個,這里就可以通過單例模式來避免兩個打印任務同時輸出到打印機中瑟押,即在整個的打印過程中我只有一個打印程序的實例搀捷。

創(chuàng)建單例

有兩種方法來創(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)建單例,而且保證了用戶不管是通過shareInstance方法怀偷,還是alloc家厌、copy方法得到的實例都是一樣的。

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

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

+ (instancetype)sharedInstance

{

if (_instance == nil) {

_instance = [[self alloc] init];

}

}

假設此時有兩條線程:線程1和線程2维蒙,都在調(diào)用shareInstance方法來創(chuàng)建單例掰吕,那么線程1運行到if (_instance == nil)出發(fā)現(xiàn)_instance = nil,那么就會初始化一個_instance,假設此時線程2也運行到if的判斷處了木西,此時線程1還沒有創(chuàng)建完成實例_instance畴栖,所以此時_instance = nil還是成立的,那么線程2又會創(chuàng)建一個_instace八千。

此時就創(chuàng)建了兩個實例對象吗讶,導致問題。

解決辦法1恋捆、使用dispatch_once

dispatch_once保證程序在運行過程中只會被運行一次照皆,那么假設此時線程1先執(zhí)行shareInstance方法,創(chuàng)建了一個實例對象沸停,線程2就不會再去執(zhí)行dispatch_once的代碼了膜毁。從而保證了只會創(chuàng)建一個實例對象。

解決辦法2、使用互斥鎖

假設此時線程1在執(zhí)行shareInstance方法瘟滨,那么synchronize大括號內(nèi)創(chuàng)建單例的代碼候醒,如下所示:

if (_instance == nil) {

if (_instance == nil) {

_instance = [[self alloc] init];

}

就會被當做一個任務被加上了一把鎖。此時假設線程2也想執(zhí)行shareInstance方法創(chuàng)建單例杂瘸,但是看到了線程1加的互斥鎖倒淫,就會進入睡眠模式。等到線程1執(zhí)行完畢败玉,才會被喚醒敌土,然后去執(zhí)行上面所示的創(chuàng)建單例的代碼,但是此時_instance !=nil,所以不會再創(chuàng)建新的實例對象了运翼。從而保證只會創(chuàng)建一個實例對象返干。

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

宏創(chuàng)建單例

如果我們需要在程序中創(chuàng)建多個單例矩欠,那么需要在每個類中都寫上一次上述代碼,非常繁瑣六剥。

我們可以使用宏來封裝單例的創(chuàng)建晚顷,這樣任何類需要創(chuàng)建單例,只需要一行代碼就搞定了疗疟。

實現(xiàn)代碼

Singleton.h文件

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)用

假設我們要在類viewcontroller中使用该默,調(diào)用方法如下:

viewcontroller.h文件

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é)果


可以看到四個對象的內(nèi)存地址完全一樣,說明是同一個對象策彤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末栓袖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子店诗,更是在濱河造成了極大的恐慌裹刮,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庞瘸,死亡現(xiàn)場離奇詭異捧弃,居然都是意外死亡,警方通過查閱死者的電腦和手機擦囊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門违霞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瞬场,你說我怎么就攤上這事买鸽。” “怎么了贯被?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵眼五,是天一觀的道長妆艘。 經(jīng)常有香客問我,道長看幼,這世上最難降的妖魔是什么批旺? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮诵姜,結(jié)果婚禮上朱沃,老公的妹妹穿的比我還像新娘。我一直安慰自己茅诱,他們只是感情好,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布搬卒。 她就那樣靜靜地躺著瑟俭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪契邀。 梳的紋絲不亂的頭發(fā)上摆寄,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音坯门,去河邊找鬼微饥。 笑死,一個胖子當著我的面吹牛古戴,可吹牛的內(nèi)容都是我干的欠橘。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼现恼,長吁一口氣:“原來是場噩夢啊……” “哼肃续!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起叉袍,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤房官,失蹤者是張志新(化名)和其女友劉穎蹭沛,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡载绿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了冷尉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片豁跑。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖转唉,靈堂內(nèi)的尸體忽然破棺而出皮钠,到底是詐尸還是另有隱情,我是刑警寧澤赠法,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布麦轰,位于F島的核電站乔夯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏款侵。R本人自食惡果不足惜末荐,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望新锈。 院中可真熱鬧甲脏,春花似錦、人聲如沸妹笆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拳缠。三九已至墩新,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窟坐,已是汗流浹背海渊。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留哲鸳,地道東北人臣疑。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像徙菠,于是被迫代替她去往敵國和親讯沈。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

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

  • 在開發(fā)中經(jīng)常會用到單例設計模式婿奔,目的就是為了在程序的整個生命周期內(nèi)芙盘,只會創(chuàng)建一個類的實例對象,而且只要程序不被殺死...
    VincentHK閱讀 638評論 0 3
  • 在開發(fā)中經(jīng)常會用到單例設計模式脸秽,目的就是為了在程序的整個生命周期內(nèi)儒老,只會創(chuàng)建一個類的實例對象,而且只要程序不被殺死...
    零度_不結(jié)冰閱讀 441評論 0 0
  • 線程間的通信 從子線程回到主線程 延時執(zhí)行 iOS常見的延時執(zhí)行有兩種方式p 調(diào)用NSObject的方法 p 使用...
    一抹月光3053閱讀 747評論 1 12
  • 進程和線程 首先记餐,在了解多線程之前要了解什么是進程驮樊,什么是線程 什么是進程呢?進程是指在系統(tǒng)中正在運行的一個應用程...
    擱淺的青蛙閱讀 360評論 0 0
  • 你曾經(jīng)想過在跑步訓練的時候练湿,自己的大腦是什么樣子的嗎?沒想過吧审轮?因為你的大部分精力都集中在配速肥哎、路況和跑姿上面辽俗。我...
    晃悠的老劉忙閱讀 782評論 1 1