一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

iOS 單例模式

 沒原創(chuàng)_去搜索 2015-06-23


創(chuàng)建一個單例很多辦法。我先列舉一個蘋果官方文檔中的寫法。

 

  1. static AccountManager *DefaultManager = nil;  
  2.    
  3. + (AccountManager *)defaultManager {  
  4.     if (!DefaultManager) DefaultManager = [[self allocWithZone:NULL] init];  
  5.     return DefaultManager;  
  6. }  


當然,在iOS4之后有了另外一種寫法:

 

 

  1. + (AccountManager *)sharedManager  
  2. {  
  3.         static AccountManager *sharedAccountManagerInstance = nil;  
  4.         static dispatch_once_t predicate;  
  5.         dispatch_once(&predicate, ^{  
  6.                 sharedAccountManagerInstance = [[self alloc] init];   
  7.         });  
  8.     return sharedAccountManagerInstance;  
  9. }  


該寫法來自 objcolumnist,文中提到,該寫法具有以下幾個特性:

 

1. 線程安全。

2. 滿足靜態(tài)分析器的要求。

3. 兼容了ARC

 

然后我還有點好奇的是dispatch_once,這個函數(shù),沒見過啊。

于是就到官方的文檔里找找看,是怎么說的。

下面是官方文檔介紹:

 

dispatch_once

Executes a block object once and only once for the lifetime of an application.

  void dispatch_once(

    dispatch_once_t *predicate,

    dispatch_block_t block);

Parameters

predicate

A pointer to a dispatch_once_t structure that is used to test whether the block has completed or not.

block

The block object to execute once.

Discussion

This function is useful for initialization of global data (singletons) in an application. Always call this function before using or testing any variables that are initialized by the block.

If called simultaneously from multiple threads, this function waits synchronously until the block has completed.

The predicate must point to a variable stored in global or static scope. The result of using a predicate with automatic or dynamic storage is undefined.

Availability

  • Available in iOS 4.0 and later.

Declared In

dispatch/once.h

 

我們看到,該方法的作用就是執(zhí)行且在整個程序的聲明周期中,僅執(zhí)行一次某一個block對象。簡直就是為單例而生的嘛。而且,有些我們需要在程序開頭初始化的動作,如果為了保證其,僅執(zhí)行一次,也可以放到這個dispatch_once來執(zhí)行。

然后我們看到它需要一個斷言來確定這個代碼塊是否執(zhí)行,這個斷言的指針要保存起來,相對于第一種方法而言,還需要多保存一個指針。

 

方法簡介中就說的很清楚了:對于在應用中創(chuàng)建一個初始化一個全局的數(shù)據(jù)對象(單例模式),這個函數(shù)很有用。

如果同時在多線程中調(diào)用它,這個函數(shù)將等待同步等待,直至該block調(diào)用結束。

這個斷言的指針必須要全局化的保存,或者放在靜態(tài)區(qū)內(nèi)。使用存放在自動分配區(qū)域或者動態(tài)區(qū)域的斷言,dispatch_once執(zhí)行的結果是不可預知的。

 

 

總結:1.這個方法可以在創(chuàng)建單例或者某些初始化動作時使用,以保證其唯一性。2.該方法是線程安全的,所以請放心大膽的在子線程中使用。(前提是你的dispatch_once_t *predicate對象必須是全局或者靜態(tài)對象。這一點很重要,如果不能保證這一點,也就不能保證該方法只會被執(zhí)行一次。)

 

轉(zhuǎn):http://blog.sina.com.cn/s/blog_69081e0601019m1z.html


====================================


大家知道,單例模式是ios里面經(jīng)常使用的模式,例如

[UIApplicationsharedApplication] (獲取當前應用程序?qū)ο?、[UIDevicecurrentDevice](獲取當前設備對象);

單例模式的寫法也很多。

第一種:

 

Java代碼  收藏代碼
  1. static Singleton *singleton = nil;  
  2.   
  3. // 非線程安全,也是最簡單的實現(xiàn)  
  4. + (Singleton *)sharedInstance  
  5. {  
  6.     if (!singleton) {  
  7.         // 這里調(diào)用alloc方法會進入下面的allocWithZone方法  
  8.         singleton = [[self alloc] init];  
  9.     }  
  10.   
  11.     return singleton;  
  12. }  
  13.   
  14.   
  15. // 這里重寫allocWithZone主要防止[[Singleton alloc] init]這種方式調(diào)用多次會返回多個對象  
  16. + (id)allocWithZone:(NSZone *)zone  
  17. {  
  18.     if (!singleton) {  
  19.         NSLog(@"進入allocWithZone方法了...");  
  20.         singleton = [super allocWithZone:zone];  
  21.         return singleton;  
  22.     }  
  23.   
  24.     return nil;  
  25. }  

 

 

 

第二種:

 

Java代碼  收藏代碼
  1. // 加入線程安全,防止多線程情況下創(chuàng)建多個實例  
  2. + (Singleton *)sharedInstance  
  3. {  
  4.     @synchronized(self)  
  5.     {  
  6.         if (!singleton) {  
  7.             // 這里調(diào)用alloc方法會進入下面的allocWithZone方法  
  8.             singleton = [[self alloc] init];  
  9.         }  
  10.     }  
  11.   
  12.     return singleton;  
  13. }  
  14.   
  15.   
  16. // 這里重寫allocWithZone主要防止[[Singleton alloc] init]這種方式調(diào)用多次會返回多個對象  
  17. + (id)allocWithZone:(NSZone *)zone  
  18. {  
  19.     // 加入線程安全,防止多個線程創(chuàng)建多個實例  
  20.     @synchronized(self)  
  21.     {  
  22.         if (!singleton) {  
  23.             NSLog(@"進入allocWithZone方法了...");  
  24.             singleton = [super allocWithZone:zone];  
  25.             return singleton;  
  26.         }  
  27.     }  
  28.       
  29.     return nil;  
  30. }  



第三種:

 

Java代碼  收藏代碼
  1. __strong static Singleton *singleton = nil;  
  2.   
  3. // 這里使用的是ARC下的單例模式  
  4. + (Singleton *)sharedInstance  
  5. {  
  6.     // dispatch_once不僅意味著代碼僅會被運行一次,而且還是線程安全的  
  7.     static dispatch_once_t pred = 0;  
  8.     dispatch_once(&pred, ^{  
  9.         singleton = [[super allocWithZone:NULL] init];  
  10.     });  
  11.     return singleton;  
  12. }  
  13. // 這里  
  14. + (id)allocWithZone:(NSZone *)zone  
  15. {  
  16.       
  17.     /* 這段代碼無法使用, 那么我們?nèi)绾谓鉀Qalloc方式呢? 
  18.      dispatch_once(&pred, ^{ 
  19.         singleton = [super allocWithZone:zone]; 
  20.         return singleton; 
  21.     }); 
  22.      */  
  23.     return [self sharedInstance];  
  24. }  
  25.   
  26. - (id)copyWithZone:(NSZone *)zone  
  27. {  
  28.     return self;  
  29. }  

第三種是ARC下單例模式,也是比較方便的, 但是[[Singleton allocinit];這種情況下就可以生成多個對象了,怎么辦呢,我們暫時先使用計數(shù)的方式來防止多次創(chuàng)建實例,如果大家有更好的方法,可以留言給我。謝謝~

 我們采用直接返回sharedInstance的方法進行創(chuàng)建。



=======================================================



單例模式的意思就是只有一個實例。單例模式確保某一個類只有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例。這個類稱為單例類。

復制代碼
#import <Foundation/Foundation.h>
 
 @interface Singleton : NSObject 
 +(Singleton *) getInstance;
@end @implementation Singleton
+(Singleton *) getInstance { static Singleton *sharedSingleton_ = nil; @synchronized(self){ if(sharedSingleton_ == nil){ sharedSingleton_ = [NSAllocateObject([self class], 0, NULL) init]; } } return sharedSingleton_; } + (id) allocWithZone:(NSZone *)zone { return [[self getInstance] retain]; } - (id) copyWithZone:(NSZone*)zone { return self; } - (id) retain { return self; } - (NSUInteger) retainCount { return NSUIntegerMax; }  
//oneway用在分布式對象的API,這些API可以在不同的線程,甚至是不同的程序。oneway關鍵字只用在返回類型為void的消息定義中, 因為oneway是異步的,其消息預計不會立即返回。
 -(oneway void)release
 {
   [super release];
 }
 
 - (id) autorelease
 {
   return self;
 }
 
 @end
復制代碼

 當然,ios 5以上啟用ARC就簡單多了:

復制代碼
static RootViewController* sharedRootController = nil;
 
+(RootViewController *) sharedController{
    @synchronized(self){
        if (sharedRootController == nil) {
           sharedRootController = [[self alloc] init];
        }
    }
    return  singleController;
}
復制代碼
 單例模式的優(yōu)缺點

1、時間和空間

比較上面兩種寫法:懶漢式是典型的時間換空間,也就是每次獲取實例都會進行判斷,看是否需要創(chuàng)建實例,浪費判斷的時間。當然,如果一直沒有人使用的話,那就不會創(chuàng)建實例,則節(jié)約內(nèi)存空間。

餓漢式是典型的空間換時間,當類裝載的時候就會創(chuàng)建類實例,不管你用不用,先創(chuàng)建出來,然后每次調(diào)用的時候,就不需要再判斷了,節(jié)省了運行時間。

2、線程安全

(1)從線程安全性上講,不加同步的懶漢式是線程不安全的,比如,有兩個線程,一個是線程A,一個是線程B,它們同時調(diào)用getInstance方法,那就可能導致并發(fā)問題。如下示例:

  1. public static  Singleton getInstance(){  
  2.     if(instance == null){  
  3.  
  4.  
  5.  
  6.         instance = new Singleton();  
  7.     }  
  8.     return instance;  

程序繼續(xù)運行,兩個線程都向前走了一步,如下:

  1. public static  Singleton getInstance(){  
  2.     if(instance == null){  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.         instance = new Singleton();  
  13.     }  
  14.     return instance;  

可能有些朋友會覺得文字描述還是不夠直觀,再來畫個圖說明一下,如圖5.4所示。

 
(點擊查看大圖)圖5.4  懶漢式單例的線程問題示意圖

通過圖5.4的分解描述,明顯地看出,當A、B線程并發(fā)的情況下,會創(chuàng)建出兩個實例來,也就是單例的控制在并發(fā)情況下失效了。

(2)餓漢式是線程安全的,因為虛擬機保證只會裝載一次,在裝載類的時候是不會發(fā)生并發(fā)的。

(3)如何實現(xiàn)懶漢式的線程安全呢?

當然懶漢式也是可以實現(xiàn)線程安全的,只要加上synchronized即可,如下:

  1. public static synchronized Singleton getInstance(){} 

但是這樣一來,會降低整個訪問的速度,而且每次都要判斷。那么有沒有更好的方式來實現(xiàn)呢?

(4)雙重檢查加鎖

可以使用"雙重檢查加鎖"的方式來實現(xiàn),就可以既實現(xiàn)線程安全,又能夠使性能不受到很大的影響。那么什么是"雙重檢查加鎖"機制呢?

所謂雙重檢查加鎖機制,指的是:并不是每次進入getInstance方法都需要同步,而是先不同步,進入方法過后,先檢查實例是否存在,如果不存在才進入下面的同步塊,這是第一重檢查。進入同步塊過后,再次檢查實例是否存在,如果不存在,就在同步的情況下創(chuàng)建一個實例,這是第二重檢查。這樣一來,就只需要同步一次了,從而減少了多次在同步情況下進行判斷所浪費的時間。

雙重檢查加鎖機制的實現(xiàn)會使用一個關鍵字volatile,它的意思是:被volatile修飾的變量的值,將不會被本地線程緩存,所有對該變量的讀寫都是直接操作共享內(nèi)存,從而確保多個線程能正確的處理該變量。

看看代碼可能會更加清楚些。示例代碼如下:

  1. public class Singleton {  
  2.     /**  
  3.      * 對保存實例的變量添加volatile的修飾  
  4.      */  
  5.     private volatile static Singleton instance = null;  
  6.     private Singleton(){  
  7.     }  
  8.     public static  Singleton getInstance(){  
  9.         //先檢查實例是否存在,如果不存在才進入下面的同步塊  
  10.         if(instance == null){  
  11.             //同步塊,線程安全地創(chuàng)建實例  
  12.             synchronized(Singleton.class){  
  13.                 //再次檢查實例是否存在,如果不存在才真正地創(chuàng)建實例  
  14.                 if(instance == null){  
  15.                     instance = new Singleton();  
  16.                 }  
  17.             }  
  18.         }  
  19.         return instance;  
  20.     }  

這種實現(xiàn)方式可以實現(xiàn)既線程安全地創(chuàng)建實例,而又不會對性能造成太大的影響。它只是在第一次創(chuàng)建實例的時候同步,以后就不需要同步了,從而加快了運行速度。

================================================================


轉(zhuǎn)自: http://2015./blog/1122130

C代碼  收藏代碼
  1. @implementation AppShareDataManager  
  2.   
  3. static AppShareDataManager * shareDataManager = nil;  
  4.   
  5. @synthesize theCurrentLanguage;  
  6. @synthesize presentModalFlag;  
  7. ..........  
  8.   
  9. +(AppShareDataManager *) sharedManager  
  10. {  
  11.     if( shareDataManager == nil )  
  12.     {  
  13.         shareDataManager = [ [ AppShareDataManager alloc ] init ];  
  14.     }  
  15.     return shareDataManager;  
  16. }  
  17.   
  18. -(id)init{  
  19.     self = [super init];  
  20.       
  21.     if(self){  
  22.         //對象實例初始化  
  23.         theCurrentLanguage = [ZONUserDefaultManager getAppDefaultLanguage];  
  24.       ........  
  25.     }  
  26.       
  27.     return self;  
  28. }  

 

 

調(diào)試發(fā)現(xiàn)

AppShareDataManager *A = [[AppShareDataManager alloc] init];

NSLog(@"A:%@",A);

AppShareDataManager *B = [AppShareDataManager sharedManager];

NSLog(@"B:%@",B);

打印出的是

 

2013-02-28 23:27:25.368 ZON2012[10647:c07] A:<AppShareDataManager: 0x12638630>

2013-02-28 23:27:25.369 ZON2012[10647:c07] B:<AppShareDataManager: 0xb584b40>

 

不是一個內(nèi)存地址,也就是不是同一個實體!

 

這是apple官方的一個單例建議:

https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32  

大致如下:

 

Java代碼  收藏代碼
  1. /* Singleton.h */  
  2. #import <Foundation/Foundation.h>  
  3.   
  4. @interface Singleton : NSObject  
  5. + (Singleton *)instance;  
  6. @end  
  7.   
  8. /* Singleton.m */  
  9. #import "Singleton.h"  
  10. static Singleton *instance = nil;  
  11.   
  12. @implementation Singleton  
  13.   
  14. + (Singleton *)instance {  
  15.     if (!instance) {  
  16.         instance = [[super allocWithZone:NULL] init];  
  17.     }  
  18.     return instance;  
  19. }  
  20.   
  21. + (id)allocWithZone:(NSZone *)zone {  
  22.     return [self instance];  
  23. }  
  24.   
  25. - (id)copyWithZone:(NSZone *)zone {  
  26.     return self;  
  27. }  
  28.   
  29. - (id)init {  
  30.     if (instance) {  
  31.         return instance;  
  32.     }  
  33.     self = [super init];  
  34.     return self;  
  35. }  
  36.   
  37. - (id)retain {  
  38.     return self;  
  39. }  
  40.   
  41. - (oneway void)release {  
  42.     // Do nothing  
  43. }  
  44.   
  45. - (id)autorelease {  
  46.     return self;  
  47. }  
  48.   
  49. - (NSUInteger)retainCount {  
  50.     return NSUIntegerMax;  
  51. }  
  52.   
  53. @end  

 這是一種很標準的Singleton實現(xiàn),中規(guī)中矩。不過這種實現(xiàn)并不是線程安全的。所以各路大神都各顯神威,給出了多種單例模式的實現(xiàn)。

 

現(xiàn)在把他加強下:

 

Java代碼  收藏代碼
  1. static MyClass *class = nil;  
  2.   
  3. @implementation MyClass  
  4.   
  5. +(MyClass *)sharedMyClass{  
  6.     @synchronized(self){  //為了確保多線程情況下,仍然確保實體的唯一性  
  7.         if (!class) {  
  8.             [[self alloc] init]; //該方法會調(diào)用 allocWithZone  
  9.         }  
  10.     }  
  11.     return class;  
  12. }  
  13. +(id)allocWithZone:(NSZone *)zone{  
  14.     @synchronized(self){// //為了確保多線程情況下,仍然確保實體的唯一性  
  15.         if (!class) {  
  16.             class = [super allocWithZone:zone]; //確保使用同一塊內(nèi)存地址  
  17.             return class;  
  18.         }  
  19.     }  
  20.     return nil;  
  21. }  
  22.   
  23. -(id)init  
  24. {  
  25.   if(class){  
  26.      return class;  
  27.   }  
  28.   if(self = [super init]){  
  29.    //進行一些初始化  
  30.    }  
  31.    return self ;  
  32. }   
  33. - (id)copyWithZone:(NSZone *)zone;{  
  34.     return self; //確保copy對象也是唯一  
  35. }  
  36.   
  37. -(id)retain{  
  38.     return self; //確保計數(shù)唯一  
  39. }  
  40.   
  41. - (unsigned)retainCount  
  42. {  
  43.    return UINT_MAX;  //裝逼用的,這樣打印出來的計數(shù)永遠為-1  
  44. }  
  45.   
  46. - (id)autorelease  
  47. {  
  48.     return self;//確保計數(shù)唯一  
  49. }   
  50.   
  51. - (oneway void)release  
  52. {  
  53.      //重寫計數(shù)釋放方法 do nothing  
  54. }  
  55. @end  

 再調(diào)試

MyClass *A = [[MyClass alloc] init];

NSLog(@"A:%@",A);

MyClass *B = [MyClass sharedMyClass];

NSLog(@"B:%@",B);

MyClass *C = [A copy];

NSLog(@"C:%@",C);

打印出的是

A:<MyClass: 0x6a1e130>

B:<MyClass: 0x6a1e130>

C:<MyClass: 0x6a1e130>

都是指向同一塊內(nèi)存地址

 

-----------------------------------切糕分割線--------------------------------------------------------

然而這個人(http:///blog/?p=178)覺的繁瑣,所以給出如下實現(xiàn):

Java代碼  收藏代碼
  1. @interface SomeManager : NSObject  
  2. + (id)sharedManager;  
  3. @end  
  4.   
  5. /* 非線程安全的實現(xiàn) */  
  6. @implementation SomeManager  
  7.   
  8. + (id)sharedManager {  
  9.     static id sharedManager = nil;  
  10.   
  11.     if (sharedManager == nil) {  
  12.         sharedManager = [[self alloc] init];  
  13.     }  
  14.   
  15.     return sharedManager;  
  16. }  
  17. @end  
  18.   
  19. /* 線程安全的實現(xiàn) */  
  20. @implementation SomeManager  
  21.   
  22. static id sharedManager = nil;  
  23.   
  24. + (void)initialize {  
  25.     if (self == [SomeManager class]) {  
  26.         sharedManager = [[self alloc] init];  
  27.     }  
  28. }  
  29.   
  30. + (id)sharedManager {  
  31.     return sharedManager;  
  32. }  
  33. @end  

 -----------------------------------切糕分割線--------------------------------------------------------

自蘋果引入了Grand Central Dispatch (GCD)(Mac OS 10.6和iOS4.0)后,利用GCD(Grand Central Dispatch)和ARC(Automatic Reference Counting)實現(xiàn)單例。

Java代碼  收藏代碼
  1. +(SchoolManager *)sharedInstance  
  2. {  
  3.     __strong static SchoolManager *sharedManager;  
  4.       
  5.     static dispatch_once_t onceToken;  
  6.     dispatch_once(&onceToken, ^{  
  7.         sharedManager = [[SchoolManager alloc] init];  
  8.     });  
  9.       
  10.     return sharedManager;  
  11. }  

 函數(shù)void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);其中第一個參數(shù)predicate,該參數(shù)是檢查后面第二個參數(shù)所代表的代碼塊是否被調(diào)用的謂詞,第二個參數(shù)則是在整個應用程序中只會被調(diào)用一次的代碼塊。dispach_once函數(shù)中的代碼塊只會被執(zhí)行一次,而且還是線程安全的。

看到如下一篇文章,用宏實現(xiàn)(https://gist.github.com/lukeredpath/1057420):

ExampleClass.m

 

@implementation MySharedThing
 
+ (id)sharedInstance
{
DEFINE_SHARED_INSTANCE_USING_BLOCK(^{
return [[self alloc] init];
});
}
 
@end

 

 

GCDSingleton.h

 

#define DEFINE_SHARED_INSTANCE_USING_BLOCK(block) \
static dispatch_once_t pred = 0; \
__strong static id _sharedObject = nil; \
dispatch_once(&pred, ^{ \
_sharedObject = block(); \
}); \
return _sharedObject; \

 



    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    午夜精品久久久99热连载| 日韩亚洲精品国产第二页| 亚洲欧洲在线一区二区三区 | 欧洲日本亚洲一区二区| 亚洲国产av一二三区| 日韩国产欧美中文字幕| 国产成人精品午夜福利| 国产中文字幕久久黄色片| 国产精品第一香蕉视频| 精品亚洲一区二区三区w竹菊| 亚洲香艳网久久五月婷婷| 99久久精品一区二区国产| 加勒比东京热拍拍一区二区| 欧美午夜不卡在线观看| 日本不卡一区视频欧美| 久久免费精品拍拍一区二区| 欧美日韩最近中国黄片| 一区二区三区四区亚洲专区| 日韩免费成人福利在线| 日韩欧美在线看一卡一卡| 一区二区欧美另类稀缺| 欧美日韩中国性生活视频| 亚洲精品黄色片中文字幕| 久久亚洲精品中文字幕| 国产内射一级二级三级| 欧美精品久久一二三区| 免费观看在线午夜视频| 亚洲熟妇av一区二区三区色堂| 日本久久精品在线观看| 日本高清不卡一二三区| 国产午夜福利一区二区| 日本午夜福利视频免费观看| 99热中文字幕在线精品| 日本熟妇五十一区二区三区| 久久人妻人人澡人人妻| 欧美中文字幕一区在线| 中文字幕有码视频熟女| 亚洲精品国产第一区二区多人| 免费黄色一区二区三区| 美国女大兵激情豪放视频播放| 九九视频通过这里有精品|