序
單例應該只用來保存全局的狀態(tài)隘膘,并且不能和任何作用域綁定仿吞。如果這些狀態(tài)的作用域比一個完整的應用程序的生命周期要短,那么這個狀態(tài)就不應該使用單例來管理半醉。用一個單例來管理用戶綁定的狀態(tài)甸祭,是代碼的壞味道碴萧,你應該認真的重新評估你的對象圖的設計囱嫩。
1苦蒿、首先說一下單例的使用
單例就是保證整個系統(tǒng)只有一個實例對象,并且自行實例化委造,向整個系統(tǒng)提供這個實例戳鹅。
單例模式的出現(xiàn)為我們帶來了很大的好處,我們可以將那些初始化比較耗費資源的操作用單例來設計昏兆,比如在我的項目中用到了藍牙枫虏,而且在不同的界面都有用到,我就把藍牙的Manager做成了一個單例爬虱,這樣只有第一次初始化藍牙模塊的時候耗時一點隶债,節(jié)省了時間。另外可以利用單例來傳值等操作跑筝。那么我們該如何創(chuàng)建單例呢死讹?
- 方法1,比較傳統(tǒng)的寫法:
+(instancetype)shared{
static LaunchIntroductionView *singleInstance = nil;
@synchronized(self) {
if (singleInstance == nil) {
singleInstance = [[LaunchIntroductionView alloc] init];
}
}
return singleInstance;
}
synchronized的使用時為了線程安全曲梗,比如有兩個線程A和B赞警,加入他們同時調(diào)用shared方法,如果不加同步的話則很可能會導致并發(fā)虏两,這樣可能就創(chuàng)建出來了兩個實例愧旦,而不是真正的單例模式了,所以需要加上同步(synchronized)定罢,來保證同一個時刻只有一個線程在訪問這個實例笤虫。這樣做是實現(xiàn)了單例,而且是線程安全的祖凫,但這樣就沒有一點問題了嗎琼蚯?問題還是有的,那就是時間的問題惠况,因為這樣寫之后我們每次調(diào)用shared都會去判斷singleInstance == nil是不是成立的凌停,這樣就避免不了浪費了判斷的時間,有人或許會說就那么一丁點的時間還用考慮嗎售滤?是的罚拟,我覺得能節(jié)省的時間我們一點都不要浪費。如果你認可了我這一點完箩,那么我們接著往下看第二種方法赐俗。
- 方法2,雙重檢查加鎖:
所謂雙重檢查機制指的是每次進入這個方法時先去判斷實例是不是為nil弊知,如果部位nil則直接返回阻逮,反之則進入同步檢查,然后再判斷是不是為nil若不存在則創(chuàng)建一個實例秩彤,專業(yè)養(yǎng)的話就只需要同步一次就行了叔扼,從而減少了同步時的判斷需要耗費的時間事哭,代碼更清晰:
+(instancetype)shared{
volatile static LaunchIntroductionView *singleInstance = nil;
if (singleInstance == nil) {
@synchronized(self) {
if (singleInstance == nil) {
singleInstance = [[LaunchIntroductionView alloc] init];
}
}
}
return singleInstance;
}
關于這一點參考自IOS單例模式及單例模式的優(yōu)缺點 ,非常感謝瓜富!之所以使用volatile修飾鳍咱,是因為被volatile修飾的變量的值不會被本地線程緩存,所有對該變量的讀寫都是直接操作共享內(nèi)存与柑,從而確保多個線程能正確的處理該變量谤辜。
- 方法3,GCD:
+(instancetype)shared{
static LaunchIntroductionView *launch = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
launch = [[LaunchIntroductionView alloc] initWithFrame:CGRectMake(0, 0, kScreen_width, kScreen_height)];
});
return launch;
}
這個就不過多介紹了价捧,網(wǎng)上的資料實在是太多了丑念。
2、單例的濫用
單例給我們帶來方便的同時也有一定的副作用结蟋,因為單例對象一旦創(chuàng)建脯倚,對象指針是保存在靜態(tài)區(qū)的,單例對象在堆中分配的內(nèi)存空間只有在程序終止后才會釋放嵌屎,過多的單例必定會增大我們消耗的內(nèi)存挠将,所以只有當我們確實需要唯一的使用對象時才需要考慮單例模式,切勿濫用單例编整,引用開頭的話:單例應該只用來保存全局的狀態(tài),并且不能和任何作用域綁定乳丰。如果這些狀態(tài)的作用域比一個完整的應用程序的生命周期要短掌测,那么這個狀態(tài)就不應該使用單例來管理。
說起來是挺容易的产园,但現(xiàn)實中對單例的濫用到處都是汞斧,一不留神就埋下了“禍根”,我在一句代碼搞定啟動引導頁中就對單例進行了濫用什燕,所以我們在使用單例的時候一定要想清楚了粘勒,我們是不是真的有必要用單例,如果這個對象的創(chuàng)建不是那么的費時費力屎即,或者這個對象沒必要再應用的整個生命周期中一直存在庙睡,那么我們是不是考慮一下?lián)Q用其他的方式,而非單例技俐?
- 參考
1乘陪、避免濫用單例
2、IOS單例模式及單例模式的優(yōu)缺點 - End