2011-09-29 56 views
27

我在網絡中發現了一些信息來使用GCD創建一個單例類。這很酷,因爲它是線程安全的,開銷很低。可悲的是我找不到完整的解決方案,但只能使用sharedInstance方法的片段。和ET瞧 - - 所以我用試錯法使我自己的類下面就出來了:正確的單例模式Objective C(iOS)?

@implementation MySingleton 

// MARK: - 
// MARK: Singleton Pattern using GCD 

+ (id)allocWithZone:(NSZone *)zone { return [[self sharedInstance] retain]; } 
- (id)copyWithZone:(NSZone *)zone { return self; } 
- (id)autorelease { return self; } 
- (oneway void)release { /* Singletons can't be released */ } 
- (void)dealloc { [super dealloc]; /* should never be called */ } 
- (id)retain { return self; } 
- (NSUInteger)retainCount { return NSUIntegerMax; /* That's soooo non-zero */ } 

+ (MySingleton *)sharedInstance 
{ 
    static MySingleton * instance = nil; 

    static dispatch_once_t predicate; 
    dispatch_once(&predicate, ^{ 
     // --- call to super avoids a deadlock with the above allocWithZone 
     instance = [[super allocWithZone:nil] init]; 
    }); 

    return instance; 
} 

// MARK: - 
// MARK: Initialization 

- (id)init 
{ 
    self = [super init]; 
    if (self) 
    { 
     // Initialization code here. 
    } 
    return self; 
} 

@end 

請隨意發表意見,並告訴我,如果我失去了一些東西或者做一些完全錯誤的;)

乾杯 斯特凡

+0

我會被誘惑添加一個引發異常的' - (void)dealloc',這樣,如果有人獲得了單例實例並釋放它,那麼您應該能夠追蹤這個有問題的actor。除了濫用模式之外,這會讓你留下一個晃來晃去的指針。 – Tommy

+0

元問題:這應該在[http://codereview.stackexchange.com/]? – Joren

+2

蘋果強烈建議不要創建單例覆蓋保留/釋放!這將打破應用程序過渡到ARC –

回答

81

保持它簡單:

+(instancetype)sharedInstance 
{ 
    static dispatch_once_t pred; 
    static id sharedInstance = nil; 
    dispatch_once(&pred, ^{ 
     sharedInstance = [[self alloc] init]; 
    }); 
    return sharedInstance; 
} 

- (void)dealloc 
{ 
    // implement -dealloc & remove abort() when refactoring for 
    // non-singleton use. 
    abort(); 
} 

就是這樣。覆蓋retainrelease,,其餘的只是隱藏bug和添加一堆不必要的代碼行。每行代碼都是一個等待發生的錯誤。實際上,如果您在共享實例上調用了dealloc,則說明您的應用中存在一個非常嚴重的錯誤。該錯誤應該修復,而不是隱藏。

此方法還適用於重構以支持非單身使用模式。幾乎每個存活超過幾個版本的單身人士最終都會被重構爲非單身人士形式。有些(如NSFileManager)繼續支持單例模式,同時還支持任意實例化。

請注意,上述也在ARC「正常工作」。

+0

謝謝你這個......只是一個關於靜態創建對象的問題。你不應該釋放那個對象我dealloc?我總是對靜態對象的所有權感到困惑。所以不應該有類似[[MyClass sharedInstance]發佈];在dealloc中? – Abolfoooud

+5

單身人士從請求到應用程序終止的那一刻就存在。它們不會被釋放並重新實例化。由於沒有理由在應用程序終止時釋放任何東西,因此沒有理由「釋放」單身人士。由於單身人士的破壞不太可能經過測試,因此如圖所示執行'dealloc'是一種純粹的防禦措施,可以提醒您未來,您以前並未考慮過此類的內存​​管理。 – bbum

+0

謝謝澄清 – Abolfoooud

19
// See Mike Ash "Care and Feeding of Singletons" 
// See Cocoa Samurai "Singletons: You're doing them wrong" 
+(MySingleton *)singleton { 
    static dispatch_once_t pred; 
    static MySingleton *shared = nil; 
    dispatch_once(&pred, ^{ 
     shared = [[MySingleton alloc] init]; 
     shared.someIvar = @"blah"; 
    }); 
    return shared; 
} 

注意dispatch_once is not reentrant,所以從dispatch_once塊內自稱會死鎖的程序。

不要試圖對自己進行防守編碼。如果你沒有編寫框架,像正常一樣對待你的類,然後堅持上面的單例成語。把單身習語看作是一種便利的方法,而不是你班級的定義特徵。您希望在單元測試期間將您的類視爲普通類,因此可以留下可訪問的構造函數。

不要打擾使用allocWithZone:

  • 它忽略它的參數和行爲方式完全一樣alloc。 Objective-C中不再使用內存區域,因此allocWithZone:僅用於與舊代碼兼容。
  • 它不起作用。您無法在Objective-C中實施單例行爲,因爲始終可以使用NSAllocateObject()class_createInstance()創建更多實例。

一個單獨的工廠方法總是返回這三種類型中的一種:

  • id以指示返回類型,沒有已知的(情況下你正在建設一個類簇)。
  • instancetype表明返回的類型是封閉類的一個實例。
  • 該類名稱本身(示例中的MySingleton)使其保持簡單。

既然你標記這個iOS版,另一種爲單是節省伊娃到應用程序的委託,然後使用一個方便的宏,如果你改變了主意,你可以重新定義:

#define coreDataManager() \ 
     ((AppDelegate*)[[UIApplication sharedApplication] delegate]).coreDataManager 
1

如果你想單元測試你單身,你也必須讓這個你可以用一個模擬單更換和/或將其重置到正常的一個:

@implementation ArticleManager 

static ArticleManager *_sharedInstance = nil; 
static dispatch_once_t once_token = 0; 

+(ArticleManager *)sharedInstance { 
    dispatch_once(&once_token, ^{ 
     if (_sharedInstance == nil) { 
      _sharedInstance = [[ArticleManager alloc] init]; 
     } 
    }); 
    return _sharedInstance; 
} 

+(void)setSharedInstance:(ArticleManager *)instance { 
    once_token = 0; // resets the once_token so dispatch_once will run again 
    _sharedInstance = instance; 
} 

@end 
+0

我在博客文章中瀏覽了代碼示例的每一行:http://twobitlabs.com/2013/01/objective-c-singleton-pattern-unit-testing/ – ToddH