一、首先說一下單例的使用緩存
單例就是保證整個系統只有一個實例對象,而且自行實例化,向整個系統提供這個實例。安全
單例模式的出現爲咱們帶來了很大的好處,咱們能夠將那些初始化比較耗費資源的操做用單例來設計,好比在個人項目中用到了藍牙,並且在不一樣的界面都有用到,我就把藍牙的Manager作成了一個單例,這樣只有第一次初始化藍牙模塊的時候耗時一點,節省了時間。另外能夠利用單例來傳值等操做。那麼咱們該如何建立單例呢?併發
方法1,比較傳統的寫法:spa
+(instancetype)shared{ static LaunchIntroductionView *singleInstance = nil; @synchronized(self) { if (singleInstance == nil) { singleInstance = [[LaunchIntroductionView alloc] init]; } } return singleInstance; }
synchronized的使用時爲了線程安全,好比有兩個線程A和B,加入他們同時調用shared方法,若是不加同步的話則極可能會致使併發,這樣可能就建立出來了兩個實例,而不是真正的單例模式了,因此須要加上同步(synchronized),來保證同一個時刻只有一個線程在訪問這個實例。這樣作是實現了單例,並且是線程安全的,但這樣就沒有一點問題了嗎?問題仍是有的,那就是時間的問題,由於這樣寫以後咱們每次調用shared都會去判斷singleInstance == nil是否是成立的,這樣就避免不了浪費了判斷的時間,有人或許會說就那麼一丁點的時間還用考慮嗎?是的,我以爲能節省的時間咱們一點都不要浪費。若是你承認了我這一點,那麼咱們接着往下看第二種方法。.net
方法2,雙重檢查加鎖:
所謂雙重檢查機制指的是每次進入這個方法時先去判斷實例是否是爲nil,若是部位nil則直接返回,反之則進入同步檢查,而後再判斷是否是爲nil若不存在則建立一個實例,專業養的話就只須要同步一次就好了,從而減小了同步時的判斷須要耗費的時間,代碼更清晰:線程
+(instancetype)shared{ volatile static LaunchIntroductionView *singleInstance = nil; if (singleInstance == nil) { @synchronized(self) { if (singleInstance == nil) { singleInstance = [[LaunchIntroductionView alloc] init]; } } } return singleInstance; }
關於這一點參考自IOS單例模式及單例模式的優缺點 ,很是感謝!之因此使用volatile修飾,是由於被volatile修飾的變量的值不會被本地線程緩存,全部對該變量的讀寫都是直接操做共享內存,從而確保多個線程能正確的處理該變量。設計
方法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; }
這個就不過多介紹了,網上的資料實在是太多了。code
二、單例的濫用
單例給咱們帶來方便的同時也有必定的反作用,由於單例對象一旦建立,對象指針是保存在靜態區的,單例對象在堆中分配的內存空間只有在程序終止後纔會釋放,過多的單例一定會增大咱們消耗的內存,因此只有當咱們確實須要惟一的使用對象時才須要考慮單例模式,切勿濫用單例,引用開頭的話:單例應該只用來保存全局的狀態,而且不能和任何做用域綁定。若是這些狀態的做用域比一個完整的應用程序的生命週期要短,那麼這個狀態就不該該使用單例來管理。
提及來是挺容易的,但現實中對單例的濫用處處都是,一不留神就埋下了「禍根」,我在一句代碼搞定啓動引導頁中就對單例進行了濫用,因此咱們在使用單例的時候必定要想清楚了,咱們是否是真的有必要用單例,若是這個對象的建立不是那麼的費時費力,或者這個對象不必再應用的整個生命週期中一直存在,那麼咱們是否是考慮一下換用其餘的方式,而非單例?對象