iOS 單例

應(yīng)用場(chǎng)景

整個(gè)程序共用一份資源時(shí)(我們只需要對(duì)這份資源初始化一次)可以使用單例例如:
1.設(shè)置單例類訪問(wèn)應(yīng)用的配置信息
2.用戶的個(gè)人信息登陸后用nsuserdefaults 存儲(chǔ)劈伴,對(duì)登錄類進(jìn)一步采用單例封裝方便全局訪問(wèn)
3.封裝一個(gè)單例對(duì)應(yīng)用多處對(duì)同一本地?cái)?shù)據(jù)庫(kù)進(jìn)行操作

使用詳解

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

作用

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

  • 有的情況下鹿寻,某個(gè)類可能只能有一個(gè)實(shí)例睦柴。比如說(shuō)你寫了一個(gè)類用來(lái)播放音樂(lè),那么不管任何時(shí)候只能有一個(gè)該類的實(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)建單例尖奔,而且保證了用戶不管是通過(guò)shareInstance方法搭儒,還是alloc、copy方法得到的實(shí)例都是一樣的越锈。

上面代碼的關(guā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í)有兩條線程:線程1和線程2稀拐,都在調(diào)用shareInstance方法來(lái)創(chuàng)建單例,那么線程1運(yùn)行到if (_instance == nil)出發(fā)現(xiàn)_instance = nil,那么就會(huì)初始化一個(gè)_instance丹弱,假設(shè)此時(shí)線程2也運(yùn)行到if的判斷處了德撬,此時(shí)線程1還沒(méi)有創(chuàng)建完成實(shí)例_instance,所以此時(shí)_instance = nil還是成立的躲胳,那么線程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í)線程1先執(zhí)行shareInstance方法,創(chuàng)建了一個(gè)實(shí)例對(duì)象恐仑,線程2就不會(huì)再去執(zhí)行dispatch_once的代碼了泉坐。從而保證了只會(huì)創(chuàng)建一個(gè)實(shí)例對(duì)象。

解決辦法2裳仆、使用互斥鎖

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

if (_instance == nil) {
_instance = [[self alloc] init];
}

就會(huì)被當(dāng)做一個(gè)任務(wù)被加上了一把鎖歧斟。此時(shí)假設(shè)線程2也想執(zhí)行shareInstance方法創(chuàng)建單例纯丸,但是看到了線程1加的互斥鎖,就會(huì)進(jì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)封裝單例的創(chuàng)建朱嘴,這樣任何類需要?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è)我們要在類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é)果

<ViewController: 0x7f897061bc00> 
<ViewController: 0x7f897061bc00> 
<ViewController: 0x7f897061bc00>
<ViewController: 0x7f897061bc00>

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

參考
http://www.cocoachina.com/cms/wap.php?action=article&id=16661

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末壤追,一起剝皮案震驚了整個(gè)濱河市磕道,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌行冰,老刑警劉巖溺蕉,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異悼做,居然都是意外死亡疯特,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門肛走,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)漓雅,“玉大人,你說(shuō)我怎么就攤上這事×谕蹋” “怎么了组题?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)吃衅。 經(jīng)常有香客問(wèn)我往踢,道長(zhǎng),這世上最難降的妖魔是什么徘层? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任峻呕,我火速辦了婚禮,結(jié)果婚禮上趣效,老公的妹妹穿的比我還像新娘瘦癌。我一直安慰自己,他們只是感情好跷敬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布讯私。 她就那樣靜靜地躺著,像睡著了一般西傀。 火紅的嫁衣襯著肌膚如雪斤寇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天拥褂,我揣著相機(jī)與錄音娘锁,去河邊找鬼。 笑死饺鹃,一個(gè)胖子當(dāng)著我的面吹牛莫秆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播悔详,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼镊屎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了茄螃?” 一聲冷哼從身側(cè)響起缝驳,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎责蝠,沒(méi)想到半個(gè)月后党巾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡霜医,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年齿拂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肴敛。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡署海,死狀恐怖吗购,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情砸狞,我是刑警寧澤捻勉,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站刀森,受9級(jí)特大地震影響踱启,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜研底,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一埠偿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧榜晦,春花似錦冠蒋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至识窿,卻和暖如春斩郎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背喻频。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工孽拷, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人半抱。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像膜宋,于是被迫代替她去往敵國(guó)和親窿侈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354