可以保證在程序運行過程,一個類只有一個實例,而且該實例易於供外界訪問
從而方便地控制了實例個數,並節約系統資源
單例模式的使用場合
在整個應用程序中,共享一份資源(這份資源只需要創建初始化1次)
簡單來說,就是我弄了一個工具類,他就有一份,比如我設計了一個音樂播放器NYPlayer,這個播放器類我就想他有一個,這就是單例,我用的時候我只需要讓播放器干活,如果多個的話我不知道指定那個,或者有重復會出bug。
百度百科是這樣說的:單例模式是一種常用的軟件設計模式。在它的核心結構中只包含一個被稱為單例類的特殊類。通過單例模式可以保證系統中一個類只有一個實例而且該實例易於外界訪問,從而方便對實例個數的控制並節約系統資源。如果希望在系統中某個類的對象只能存在一個,單例模式是最好的解決方案。
簡單來說,就是讓一個類的事物裡面只有一個對象。
過程:
在.m中保留一個全局的static的實例
這裡要用static,記住他,稍後我回寫一個關於static關鍵詞的博客。
<code class="hljs objectivec has-numbering"><span class="hljs-keyword">static</span> <span class="hljs-keyword">id</span> _instance;</code>
重寫allocWithZone:方法,在這裡創建唯一的實例(注意線程安全)
創建類的時候調用[類名 alloc]方法的時候,內部會自動調用allcWithZone方法,加載一個空間,所以我們只要重寫這個方法,當我們定義的全局變量instance有值得時候,就會直接返回,如果沒有,就會調用父類方法[super allocWithZone:zone]並賦值給instance,但是這時候有一個危險,那就是如果這裡有兩個線程都進來了,那樣就會分配兩次,所以我們要給本段程序加鎖。
<code class="hljs objectivec has-numbering">+ (<span class="hljs-keyword">id</span>)allocWithZone:(<span class="hljs-keyword">struct</span> _NSZone *)zone { <span class="hljs-keyword">if</span> (_instance == <span class="hljs-literal">nil</span>) { <span class="hljs-comment">// 防止頻繁加鎖</span> @synchronized(<span class="hljs-keyword">self</span>) { <span class="hljs-keyword">if</span> (_instance == <span class="hljs-literal">nil</span>) { <span class="hljs-comment">// 防止創建多次</span> _instance = [<span class="hljs-keyword">super</span> allocWithZone:zone]; } } } <span class="hljs-keyword">return</span> _instance; }</code>
提供一個類方法讓外界訪問唯一的實例。
跟上面的allocWithzone一樣,不過有所不同的是這裡調用的時【[self alloc] init】方法。
<code class="language-oc hljs objectivec has-numbering">+ (instancetype)sharedMusicTool { <span class="hljs-keyword">if</span> (_instance == <span class="hljs-literal">nil</span>) { <span class="hljs-comment">// 防止頻繁加鎖</span> @synchronized(<span class="hljs-keyword">self</span>) { <span class="hljs-keyword">if</span> (_instance == <span class="hljs-literal">nil</span>) { <span class="hljs-comment">// 防止創建多次</span> _instance = [[<span class="hljs-keyword">self</span> alloc] init]; } } } <span class="hljs-keyword">return</span> _instance; } 實現copyWithZone:方法 - (<span class="hljs-keyword">id</span>)copyWithZone:(<span class="hljs-keyword">struct</span> _NSZone *)zone { <span class="hljs-keyword">return</span> _instance; } </code>
過程:
非ARC中(MRC),單例模式的實現(比ARC多了幾個步驟)
實現內存管理方法。
<code class="hljs haml has-numbering">-<span class="ruby"> (id)retain { <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>; }/<span class="hljs-regexp">/父類裡面是計數器+1,重寫將不會+1 </span></span>-<span class="ruby"> (<span class="hljs-constant">NSUInteger</span>)retainCount { <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>; }/<span class="hljs-regexp">/父類裡面返回當前對象的計數個數 </span></span>-<span class="ruby"> (oneway void)release {}/<span class="hljs-regexp">/父類裡面是計數器-1,重寫將不會-1 </span></span>-<span class="ruby"> (id)autorelease { <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>; }/<span class="hljs-regexp">/父類裡面內存自動管理釋放的,跟內存池有關</span></span></code>
這裡首先要知道一個宏了 __has_feature(objc_arc)
單例模式在ARCMRC環境下的寫法有所不同,需要編寫2套不同的代碼
可以用宏判斷是否為ARC環境。
<code class="hljs cs has-numbering"><span class="hljs-preprocessor">#<span class="hljs-keyword">if</span> __has_feature(objc_arc)</span> <span class="hljs-comment">// ARC</span> <span class="hljs-preprocessor">#<span class="hljs-keyword">else</span></span> <span class="hljs-comment">// MRC</span> <span class="hljs-preprocessor">#<span class="hljs-keyword">endif</span></span></code>
就一句
<code class="hljs cs has-numbering"><span class="hljs-preprocessor">#<span class="hljs-keyword">define</span> NYSingletonH(name) + (instancetype)shared##name;</span></code>
用的時候傳進name,他就會自動替換了。
怎麼解決呢 ?其實就是把加鎖換成GCD的一次性代碼
代碼如下:
<code class="hljs objectivec has-numbering">+(<span class="hljs-keyword">id</span>)allocWithZone:(<span class="hljs-keyword">struct</span> _NSZone *)zone { <span class="hljs-keyword">if</span> (_instance == <span class="hljs-literal">nil</span>) { <span class="hljs-keyword">static</span> <span class="hljs-built_in">dispatch_once_t</span> onceToken; <span class="hljs-built_in">dispatch_once</span>(&onceToken, ^{ <span class="hljs-keyword">if</span> (_instance == <span class="hljs-literal">nil</span>) { _instance = [<span class="hljs-keyword">super</span> allocWithZone:zone]; } }); } <span class="hljs-keyword">return</span> _instance; } </code>
快捷方式,xcode中只要敲dispatch_once就自動有啦,填填寫寫。
第一步,簡歷一個.h文件,例如
第二步,文件中寫入以下代碼:
<code class="hljs tex has-numbering">// .h文件 <span class="hljs-special">#</span>define NYSingletonH(name) + (instancetype)shared<span class="hljs-special">#</span><span class="hljs-special">#</span>name; // .m文件 <span class="hljs-special">#</span>define NYSingletonM <span class="hljs-special">#</span>if __has_feature(objc_arc) <span class="hljs-special">#</span>define NYSingletonM(name)<span class="hljs-command"> </span> static id _instance;<span class="hljs-command"> </span><span class="hljs-command"> </span> +(id)allocWithZone:(struct _NSZone *)zone<span class="hljs-command"> </span> <span class="hljs-special">{</span><span class="hljs-command"> </span> if (_instance == nil) <span class="hljs-special">{</span><span class="hljs-command"> </span> static dispatch_once_t onceToken;<span class="hljs-command"> </span> dispatch_once(<span class="hljs-special">&</span>onceToken, ^<span class="hljs-special">{</span><span class="hljs-command"> </span> if (_instance == nil) <span class="hljs-special">{</span><span class="hljs-command"> </span> _instance = <span class="hljs-special">[</span>super allocWithZone:zone<span class="hljs-special">]</span>;<span class="hljs-command"> </span> <span class="hljs-special">}</span><span class="hljs-command"> </span> <span class="hljs-special">}</span>);<span class="hljs-command"> </span> <span class="hljs-special">}</span><span class="hljs-command"> </span> return _instance;<span class="hljs-command"> </span> <span class="hljs-special">}</span><span class="hljs-command"> </span><span class="hljs-command"> </span> +(instancetype)shared<span class="hljs-special">#</span><span class="hljs-special">#</span>name<span class="hljs-command"> </span> <span class="hljs-special">{</span><span class="hljs-command"> </span> if (_instance == nil) <span class="hljs-special">{</span><span class="hljs-command"> </span> static dispatch_once_t onceToken;<span class="hljs-command"> </span> dispatch_once(<span class="hljs-special">&</span>onceToken, ^<span class="hljs-special">{</span><span class="hljs-command"> </span> if (_instance == nil) <span class="hljs-special">{</span><span class="hljs-command"> </span> _instance = <span class="hljs-special">[</span><span class="hljs-special">[</span>self alloc<span class="hljs-special">]</span>init<span class="hljs-special">]</span>;<span class="hljs-command"> </span> <span class="hljs-special">}</span><span class="hljs-command"> </span> <span class="hljs-special">}</span>);<span class="hljs-command"> </span> <span class="hljs-special">}</span><span class="hljs-command"> </span> return _instance;<span class="hljs-command"> </span> <span class="hljs-special">}</span><span class="hljs-command"> </span><span class="hljs-command"> </span> -(id)copyWithZone:(NSZone *)zone<span class="hljs-command"> </span> <span class="hljs-special">{</span><span class="hljs-command"> </span> return _instance;<span class="hljs-command">\</span> <span class="hljs-special">}</span> <span class="hljs-special">#</span>else <span class="hljs-special">#</span>define NYSingletonM(name)<span class="hljs-command"> </span> static id _instance;<span class="hljs-command"> </span><span class="hljs-command"> </span> +(id)allocWithZone:(struct _NSZone *)zone<span class="hljs-command"> </span> <span class="hljs-special">{</span><span class="hljs-command"> </span> if (_instance == nil) <span class="hljs-special">{</span><span class="hljs-command"> </span> static dispatch_once_t onceToken;<span class="hljs-command"> </span> dispatch_once(<span class="hljs-special">&</span>onceToken, ^<span class="hljs-special">{</span><span class="hljs-command"> </span> if (_instance == nil) <span class="hljs-special">{</span><span class="hljs-command"> </span> _instance = <span class="hljs-special">[</span>super allocWithZone:zone<span class="hljs-special">]</span>;<span class="hljs-command"> </span> <span class="hljs-special">}</span><span class="hljs-command"> </span> <span class="hljs-special">}</span>);<span class="hljs-command"> </span> <span class="hljs-special">}</span><span class="hljs-command"> </span> return _instance;<span class="hljs-command"> </span> <span class="hljs-special">}</span><span class="hljs-command"> </span><span class="hljs-command"> </span> +(instancetype)shared<span class="hljs-special">#</span><span class="hljs-special">#</span>name<span class="hljs-command"> </span> <span class="hljs-special">{</span><span class="hljs-command"> </span> if (_instance == nil) <span class="hljs-special">{</span><span class="hljs-command"> </span> static dispatch_once_t onceToken;<span class="hljs-command"> </span> dispatch_once(<span class="hljs-special">&</span>onceToken, ^<span class="hljs-special">{</span><span class="hljs-command"> </span> if (_instance == nil) <span class="hljs-special">{</span><span class="hljs-command"> </span> _instance = <span class="hljs-special">[</span><span class="hljs-special">[</span>self alloc<span class="hljs-special">]</span>init<span class="hljs-special">]</span>;<span class="hljs-command"> </span> <span class="hljs-special">}</span><span class="hljs-command"> </span> <span class="hljs-special">}</span>);<span class="hljs-command"> </span> <span class="hljs-special">}</span><span class="hljs-command"> </span> return _instance;<span class="hljs-command"> </span> <span class="hljs-special">}</span><span class="hljs-command"> </span><span class="hljs-command"> </span> -(id)copyWithZone:(NSZone *)zone<span class="hljs-command"> </span> <span class="hljs-special">{</span><span class="hljs-command"> </span> return _instance;<span class="hljs-command"> </span> <span class="hljs-special">}</span><span class="hljs-command"> </span><span class="hljs-command"> </span> -(id)retain<span class="hljs-command"> </span> <span class="hljs-special">{</span><span class="hljs-command"> </span> return self;<span class="hljs-command"> </span> <span class="hljs-special">}</span><span class="hljs-command"> </span> -(NSUInteger)retainCount<span class="hljs-special">{</span>return 1;<span class="hljs-special">}</span><span class="hljs-command"> </span> -(oneway void)release<span class="hljs-special">{</span><span class="hljs-special">}</span><span class="hljs-command"> </span> -(id)autorelease<span class="hljs-special">{</span>return self;<span class="hljs-special">}</span> <span class="hljs-special">#</span>endif</code>
符號是讓後面的東東包含到上一行去,因為define只能定義一行。
第三步:在pch文件中寫入調用代碼:
<code class="hljs ruleslanguage has-numbering"><span class="hljs-array">#import </span><span class="hljs-string">"NYSingleton.h"</span></code>
第四步:在需要用到的類中調用:
在點h文件中調用這個
在點m文件中調用這個
最後調用,還是單例:
注意:開發過程中,會使用NSLog來打印信息用於調試,但releae的軟件卻不能包含NSLog,可能會有被打回的風險,但是要是全部注釋掉NSLog那太痛苦了,也不利於以後的調試。
下面,我們可以采用自定義宏來取代NSLog,只在我們想要的時候輸出
<code class="hljs vala has-numbering"> <span class="hljs-comment">/* XCode LLVM XXX - Preprocessing中Debug會添加 DEBUG=1 標志 */</span> <span class="hljs-preprocessor">#ifdef DEBUG</span> <span class="hljs-preprocessor">#define NSLog(FORMAT, ...) fprintf(stderr,"%s:%dt%sn",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);</span> <span class="hljs-preprocessor">#else</span> <span class="hljs-preprocessor">#define NSLog(FORMAT, ...) nil</span> <span class="hljs-preprocessor">#endif</span></code>
把以上代碼粘貼到ProjectName-Prefix.pch文件中。
在調試的時候,會輸出(格式:文件名:行號)日志。
在Release正式版本的時候,會關閉日志輸出。
因為XCode LLVM XXX - Preprocessing中Debug會添加 DEBUG=1 標志。當然我們也能把DEBUG直接換成數字1來設置。