在我認(rèn)為單例最大的優(yōu)點(diǎn)就是:通過單例方式訪問的實(shí)例,都是同一個(gè)沼本。
這個(gè)優(yōu)點(diǎn)也是我在藍(lán)牙音樂盒項(xiàng)目中使用單例模式實(shí)現(xiàn)外設(shè)控制類和AVFoundation類的原因噩峦。
你們想锭沟,假設(shè)我在a,b,c頁面都會調(diào)用到_player,那么我再a頁面點(diǎn)擊播放抽兆、上一首、下一首等等操作后族淮,怎么讓b,c頁面知道并同步呢辫红?我一開始使用的是通知。可是我試了后發(fā)現(xiàn),最好的還是單例行疏。
單例模式下的無論在那個(gè)類訪問_player實(shí)例精算,它的狀態(tài)都是最新的,也就是a點(diǎn)了播放续捂,那么我在b,c頁面訪問_play實(shí)例,就可以知道它的狀態(tài)是播放的预厌,不需要通知娩鹉。
創(chuàng)建單例:
.h
+ (CustomObject *)share;
.m
+ (CustomObject *)share{
static CustomObject *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[CustomObject alloc] init];
});
return instance;
}
代碼解釋:鏈接
dispatch_once主要根據(jù)onceToken的值來決定是否執(zhí)行block里的方法攻谁,如果onceToken==0,線程執(zhí)行block里的方法,當(dāng)onceToken==-1弯予,線程不執(zhí)行block戚宦。當(dāng)onceToken為其他值時(shí),線程被阻塞锈嫩,等待onceToken值改變受楼。
當(dāng)線程首先調(diào)用instance,某一線程要執(zhí)行block中的代碼時(shí)呼寸,首先需要改變onceToken的值艳汽,再去執(zhí)行block中的代碼。這里onceToken的值變?yōu)榱?40734731430192等舔。這樣當(dāng)其他線程再獲取onceToken的值時(shí)骚灸,值已經(jīng)變?yōu)?40734731430192。其他線程被阻塞慌植。當(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)方案
通過下面代碼可以理解單例的優(yōu)點(diǎn):
viewController.m
- (void)viewDidLoad {
[super viewDidLoad];
CustomObject *cv = [CustomObject share];
cv.tempString = @"123";
}
SecondView.m
- (void)viewDidLoad {
[super viewDidLoad];
CustomObject *cv = [CustomObject share];
NSLog(@"%@",cv.tempString);
}
CocoaPodsDemo[87052:9724995] 123
可以看到:
1.獲取CustomObject對象的實(shí)例很方便蜜宪。
2.在兩個(gè)類里的實(shí)例變量實(shí)際上是同一個(gè)圃验,沒有重新alloc空間,節(jié)省了內(nèi)存缝呕。當(dāng)然澳窑,通過單例創(chuàng)建的實(shí)例常駐內(nèi)存,所以資源開銷就見仁見智供常。
3.獲取實(shí)例的狀態(tài)非常容易摊聋。因?yàn)槭峭粋€(gè)實(shí)例,當(dāng)我在第一個(gè)類里改變了tempString
狀態(tài)為@"123"
,我再第二個(gè)類想要獲取tempString
的狀態(tài)馬上就變成了@"123"
栈暇。
第三條原因讓單例使用起來非常爽麻裁,也是我認(rèn)為單例最大的優(yōu)點(diǎn),這我一開始就說過了。
注意:
訪問單例類一定要通過定義的單例方法+ (CustomObject *)share
源祈。如果依舊使用alloc煎源、init或者new就會發(fā)生下面的情況
viewController.h
- (void)viewDidLoad {
[super viewDidLoad];
CustomObject *cv = [CustomObject share];
cv.tempString = @"123";
}
SecondView.h
- (void)viewDidLoad {
[super viewDidLoad];
CustomObject *cv = [[CustomObject alloc] init];
NSLog(@"%@",cv.tempString);
}
CocoaPodsDemo[87052:9724995] (null)
解釋:
上面兩個(gè)類里獲得的實(shí)例其實(shí)不是同一個(gè),因?yàn)橥ㄟ^單例方式獲得實(shí)例本來就是通過封鎖alloc init方法實(shí)現(xiàn)的香缺,但是我再SecondView重新使用了alloc/init方法實(shí)例化了手销。
那么如何避免呢?下面是我想的一種方法图张,希望指點(diǎn):
- (instancetype)init{
return [CustomObject share];
}
- (instancetype)initHidden{
self = [super init];
if (self) {
}
return self;
}
+ (CustomObject *)share{
static CustomObject *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[CustomObject alloc] initHidden];
});
return instance;
}