製作Singleton
Cocoa Samurai的 Singletons: You’re doing them wrong 這篇文章提到了一種良好的singleton建構方式。
+(MyClass *)sharedInstance {
static dispatch_once_t pred;
static MyClass *shared = nil;
dispatch_once(&pred, ^{
shared = [[MyClass alloc] init];
});
return shared;
}
這個寫法的優點有三
1. 在整個app生命週期裡只會被執行一次
2. thread safe
3. 比用@synchronize()快
測試Singleton
現在想像有一個class用到了這個singleton,而當我們要對這個class做unit test,測試是否如我們預期般運作的時候問題就來了。在做unit test的時候,受測物件必須隔離與其他物件的相依性,否則就變成integration test了。我們通常會用mock達成這個目的。
例如我們有下面這段code
- (void)doSomething
{
[[ArticleManager sharedInstance] doSomethingElse];
...
}
顯然的,它只會取得唯一存在的instance,沒有讓你mock的機會。這時可以採用 Objective-C Singleton Pattern Updated For Testability 這篇文章提到的方法,把singleton做一點變形。
@implementation ArticleManager
static ArticleManager *_sharedInstance = nil;
static dispatch_once_t once_token = 0;
+(instancetype)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
在做測試的時候
id mockManager = [OCMockObject mockForClass:[ArticleManager class]];
[ArticleManager setSharedInstance:mockManager]; //把instance換成我們的mock
/*
在這裡做測試
*/
[ArticleManager setSharedInstance:nil]; //還原
這樣就可以使用自己的mock了。
沒有留言:
張貼留言