標籤:c style class blog code a
iOS中單例模式的實現一般分為兩種:MRC和ARC+GCD
1.MRC(非ARC)
非ARC的單例的實現方式:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<span style= "font-family: 仿宋; font-size: 15px;" >#import <Foundation/Foundation.h> @interface NoARCSingleton: NSObject <span> //這個屬性在後面調試有用處,而且也不要苦惱為什麼是retain?不應該是copy嗎?請繼續看下去,後面自然明白了<br></span>@property (nonatomic,retain) NSString *tempProperty; +(NoARCSingleton *)sharedInstance; @end @implementation NoARCSingleton static NoARCSingleton *sharedInstance = nil ; //擷取一個單例對象 + (BVNonARCSingleton *)sharedInstance {<br> //--------------------------------------------------<br> if (sharedInstance == nil) { sharedInstance = [[ super allocWithZone: NULL ] init]; } return sharedInstance;<br> //-------------------------------------------------- } //當第一次使用這個單例的時候,會調用這個init方法。 -( id )init { self = [ super init]; if ( self ){ //通常在這裡做一些相關的初始化任務 return self ; } } //這個delloc方法永遠都不會被調用,因為在程式的生命週期內,該單例都必須存在,該方法可以不用實現 -( void )delloc { [ super dealloc]; } //通過返回當前的單例對象的方式來防止執行個體化新的對象 + ( id )allocWithZone:( NSZone *)zone {<br> //線程相關的操作安全有</span>sharedInstance處理 |
?
1 2 3 4 5 6 7 |
<span style= "font-family: 仿宋; font-size: 15px;" > return [[ self sharedInstance] retain]; } <br> //同樣的,不希望產生單例的多個拷貝,必須重寫 <br>- (id)copyWithZone:(NSZone *)zone { return self; } <br>//這個方法中什麼操作都不需要該單例並不需要一個引用計數(retain counter)<br> -(id)retain { return self; }<br> //替換掉引用計數器,這樣永遠都不會release這個單例了<br> - (NSUInteger)retainCount { return NSUIntegerMax; } <br>// 該方法是空的——不希望使用者release掉這個對象。<br> -(oneway void)release{ }<br> //除了返回單例外,什麼也不做。<br> -(id)autorelease { return self; } <br>@end <br>//@synchronized 的作用是建立一個互斥鎖,保證此時沒有其它線程對self對象進行修改。這個是objective-c的一個鎖定令牌,防止self對象在同一時間內被其它線程訪問,起到線程的保護作用。 一般在公用變數的時候使用,如單例模式或者操作類的static變數中使用。<br>//非ARC實現單例的方法是線程不安全的,如果有多個線程同時調用<span>sharedInstance方法擷取一個執行個體,而<span>sharedInstance需要花費1-2s的時間,那麼<span>NonARCSingleton的init方法可能會被多次的調用,也就是多個線程獲得的單例有可能不是一個單例,解決這個線程不安全的方式是使用<a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html" target="_blank"><span class="edui-filter-underline">@synchronized</span></a><span>來建立互斥鎖即可。<br>//<span>當然,該方法不能保證該單例中所有方法的調用都是安全執行緒的</span><br></span></span></span></span>//所以上面的橫線之間的代碼應該換成下面的代碼 @synchronized ( self )</span> { if (sharedInstance == nil ) { sharedInstance = [[ super allocWithZone: NULL ] init]; }<br>#param mark <span style= "font-family: 仿宋; font-size: 15px;" >-提醒:在iOS中,一般不建議使用非ARC來實現單例模式。更好的方法是使用ARC+GCD來實現。</span> |
ARC實現的方式
?
1 2 3 4 5 6 7 |
<span style= "font-family: 仿宋; font-size: 15px;" >#import "ARCSingleton.h" @interface ARCSingleton : NSObject <br> //調試之用和上面的代碼作用類似的,這裡為什麼用weak呢?請繼續看下去~<br><span>@property ( nonatomic, weak) NSString *tempProperty;</span> + (ARCSingleton *)sharedInstance; @end @implementation ARCSingleton + (ARCSingleton *) sharedInstance {<br><span style= "line-height: 1.5;" > static ARCSingleton *sharedInstance = nil ;</span></span> |
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<span style= "font-family: 仿宋; font-size: 15px;" > static dispatch_once_t onceToken = 0; // 鎖 dispatch_once (&onceToken, ^ { // 最多調用一次 sharedInstance = [[ self alloc] init]; }); return sharedInstance; } //當第一次使用這個單例時,會調用這個init方法 -( id )init { self = [ super init]; if ( self ){ //初始化單例 } return self ; } @end <br> //<span>在上面的代碼中,調用</span><span class="edui-filter-underline"><a href="http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html" target="_blank">Grand Central Dispatch <br>(GCD)</a></span><span>中的</span><span class="edui-filter-underline"><a href="https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_once" target="_blank">dispatch_once</a></span><span>方法就可以確保ARCSingleton只被執行個體化一次。並且該方法是安全執行緒的,我們不用擔心在不同的線程中,會獲得不同的執行個體。(當然,該方法同樣不能保證該單例中所有方法的調用都是安全執行緒的)。</span><br> </span> |
當然在ARC中,不用GCD也是可以做到安全執行緒的,跟之前非ARC代碼中使用@synchronized一樣,如下代碼:
?
1 2 3 4 5 6 7 8 9 |
<span style= "font-family: 仿宋; font-size: 15px;" > // 不使用GCD,通過@synchronized @synchronized ( self ) { if (sharedInstance == nil ) { sharedInstance = [[ self alloc] init]; } } </span> |
為了簡化使用ARC+GCD來建立單例,可以使用下面這個宏
?
1 2 3 4 5 6 7 8 9 10 11 |
#define DEFINE_SHARED_INSTANCE_USING_BLOCK(block) \ static dispatch_once_t onceToken = 0; \ static id sharedInstance = nil ; \ dispatch_once(&onceToken, ^{ \ sharedInstance = block(); \}); \ return sharedInstance; \<br><span style= "font-family: 仿宋; font-size: 15px;" > //如果對於macro有問題的話強烈建議去腦補下貓神的文章http://onevcat.com/2014/01/black-magic-in-macro/<br>//另外貓神的很多文章都是很深入的建議有時間多去細細品味</span> |
宏寫完了之後那就so easy了!
執行個體化方法實現:
?
1 2 3 4 5 6 |
+ (BVARCSingleton *) sharedInstance { DEFINE_SHARED_INSTANCE_USING_BLOCK(^{ return [[ self alloc] init]; }); }<br> //Done |
單例的使用:單例的使用方法很簡單,在代碼中的任意位置,如下使用即可:
在BVAppDelegate.m中添加標頭檔:
#import "NonARCSingleton.h"
#import "ARCSingleton.h"
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
- ( BOOL )application:(UIApplication *)application didFinishLaunchingWithOptions:( NSDictionary *)launchOptions { [NonARCSingleton sharedInstance].tempProperty = @ "非ARC單例的實現" ; NSLog (@ "%@" , [BVNonARCSingleton sharedInstance].tempProperty); [ARCSingleton sharedInstance].tempProperty = @ "ARC單例的實現" ; NSLog (@ "%@" , [ARCSingleton sharedInstance].tempProperty); return YES ; } |
附加乾貨
__weak,__strong
很少會見到 __weak 和 __strong 出現在聲明中,但我們需要對它們有一定的瞭解。
預設情況下,一個指標都會使用 __strong 屬性,表明這是一個強引用。這意味著,只要引用存在,對象就不能被銷毀。這是一種所期望的行為:當所有(強)引用都去除時,對象才能被收集和釋放。不過, 有時我們卻希望禁用這種行為:一些集合類不應該增加其元素的引用,因為這會引起對象無法釋放。在這種情況下,我們需要使用弱引用(不用擔心,內建的集合類 就是這麼乾的),使用 __weak 關鍵字。NSHashTable 就是一個例子。當被引用的對象消失時,弱引用會自動化佈建為 nil。Cocoa 的 nsnotificationcenter 就是這麼一個例子,雖然這已經超出純 Objective-C 的語言範疇 .