objective-c 单例的线程安全实例化
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2199106/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Thread safe instantiation of a singleton
提问by MacTouch
Which one synchronization method to use to ensure a singleton remains a singleton?
使用哪种同步方法来确保单例仍然是单例?
+(Foo*)sharedInstance
{
@synchronized(self)
{
if (nil == _sharedInstance)
{
_sharedInstance = [[Foo alloc] init];
...
}
}
return _sharedInstance;
}
or using a mutex?
或使用互斥锁?
#import <pthread.h>
static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
+(Foo*)sharedInstance
{
pthread_mutex_lock(&_mutex);
if (nil == _sharedInstance)
{
_sharedInstance = [[Foo alloc] init];
...
}
pthread_mutex_unlock(&_mutex);
return _sharedInstance;
}
Hmmm.. any comments on this?
嗯.. 对此有何评论?
回答by bbum
Make sure you read the discussion on this question/answer, too. Why should we separate alloc and init calls to avoid deadlocks in Objective-C?
请确保您也阅读有关此问题/答案的讨论。 为什么我们应该将 alloc 和 init 调用分开来避免 Objective-C 中的死锁?
To expand on the race condition issue; the realfix is to not have indeterminate initialization within your application. Indeterminateor lazyinitialization results in behavior that can easily change due to seemingly innocuous changes -- configuration, "unrelated" code changes, etc...
扩展竞争条件问题;在真正的解决办法是你的应用程序中没有不确定初始化。 不确定或懒惰的初始化导致行为很容易由于看似无害的变化而改变——配置、“不相关”的代码更改等......
Better to explicitly initialize subsystems on a known-good point in the program's lifespan. I.e. drop [MyClass sharedInstance];into your App delegate's applicationDidFinishLaunching:method if you reallyneed that subsystem initialized early in the program (or move it even earlier, if you want to be extra defensive).
最好在程序生命周期中的已知良好点上显式初始化子系统。即,如果您确实需要在程序早期初始化该子系统(或者更早移动它,如果您想要额外的防御),则[MyClass sharedInstance];进入您的 App 委托的applicationDidFinishLaunching:方法。
Better still to move initialization out of that method entirely. I.e. [MyClass initializeSharedInstance];where +sharedInstanceasserts() if that method isn't called first.
最好将初始化完全移出该方法。即[MyClass initializeSharedInstance];,+sharedInstance如果该方法没有首先被调用,那么 asserts()在哪里。
As much as I'm a a fan of convenience, 25 years of ObjC programming has taught me that lazy initialization is a source of more maintenance and refactoring headaches than it is worth.
尽管我是一个方便的粉丝,但 25 年的 ObjC 编程告诉我,延迟初始化是更多维护和重构问题的根源,而不是它的价值。
While the race condition described below exists, this code doesn't fix what is described below. It did for a couple of decades when we didn't worry about concurrency in shared instance initializers. Leaving the wrong code for prosperity.
虽然下面描述的竞争条件存在,但这段代码并没有解决下面描述的问题。当我们不担心共享实例初始化程序中的并发性时,它已经持续了几十年。为繁荣留下错误的密码。
Keep in mind that for both Colin's and Harald's otherwise correct answers, there is a very subtle race condition that could lead you to a world of woe.
请记住,对于 Colin 和 Harald 的其他正确答案,有一个非常微妙的种族条件可能会导致您陷入困境。
Namely, if the -initof the class being allocated happens to call the sharedInstancemethod, it will do so before the variable is set. In both cases it will lead to a deadlock.
也就是说,如果-init被分配的类的 碰巧调用了该sharedInstance方法,它将在设置变量之前调用。这两种情况都会导致死锁。
This is the one time that you want to separate the alloc and the init. Cribbing Colin's code because it is the best solution (assuming Mac OS X):
这是您想要将 alloc 和 init 分开的一次。抄袭 Colin 的代码,因为它是最好的解决方案(假设是 Mac OS X):
+(MyClass *)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t pred;
// partial fix for the "new" concurrency issue
if (sharedInstance) return sharedInstance;
// partial because it means that +sharedInstance *may* return an un-initialized instance
// this is from https://stackoverflow.com/questions/20895214/why-should-we-separate-alloc-and-init-calls-to-avoid-deadlocks-in-objective-c/20895427#20895427
dispatch_once(&pred, ^{
sharedInstance = [MyClass alloc];
sharedInstance = [sharedInstance init];
});
return sharedInstance;
}
notethis only works on Mac OS X; X 10.6+ and iOS 4.0+, in particular. On older operating systems, where blocks are not available, use a lock or one of the various means of doing something once that isn't blocks based.
请注意,这仅适用于 Mac OS X;特别是 X 10.6+ 和 iOS 4.0+。在旧的操作系统上,块不可用,请使用锁或多种方式之一来做一些不是基于块的事情。
The above pattern does not actually prevent the problem described in the text and will cause a deadlock when it is encountered. The problem is that the dispatch_once()is not re-entrant and, thus, if the initcalls sharedInstance, wedge city.
上述模式实际上并不能防止文中描述的问题,遇到时会造成死锁。问题是dispatch_once()不是可重入的,因此,如果init调用sharedInstance,楔形城市。
回答by Colin Wheeler
The fastest thread safe way to do this is with Grand Central Dispatch ( libdispatch ) and dispatch_once()
最快的线程安全方法是使用 Grand Central Dispatch ( libdispatch ) 和 dispatch_once()
+(MyClass *)sharedInstance
{
static MyClass *sharedInstance = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
sharedInstance = [[MyClass alloc] init];
});
return sharedInstance;
}
回答by Arvin
If anyone cares, here is a Macro for the same thing:
如果有人在乎,这里有一个宏来做同样的事情:
/*!
* @function Singleton GCD Macro
*/
#ifndef SINGLETON_GCD
#define SINGLETON_GCD(classname) \
\
+ (classname *)shared##classname { \
\
static dispatch_once_t pred; \
static classname * shared##classname = nil; \
dispatch_once( &pred, ^{ \
shared##classname = [[self alloc] init]; \
}); \
return shared##classname; \
}
#endif
回答by Laurent Etiemble
This CocoaDev pagecan be useful for your need.
这个CocoaDev 页面可以满足您的需求。
回答by skozin
If anyone cares, here is another macro for the same thing :)
如果有人在乎,这里是同一件事的另一个宏:)
IMHO, it provides greater flexibility compared to the other variations.
恕我直言,与其他变体相比,它提供了更大的灵活性。
#define SHARED_INSTANCE(...) ({\
static dispatch_once_t pred;\
static id sharedObject;\
dispatch_once(&pred, ^{\
sharedObject = (__VA_ARGS__);\
});\
sharedObject;\
})
Usage, one-line initialization:
用法,一行初始化:
+ (instancetype) sharedInstanceOneLine {
return SHARED_INSTANCE( [[self alloc] init] );
}
Usage, multi-line initialization (notice curly braces around the block of code):
用法,多行初始化(注意代码块周围的花括号):
+ (instancetype) sharedInstanceMultiLine {
return SHARED_INSTANCE({
NSLog(@"creating shared instance");
CGFloat someValue = 84 / 2.0f;
[[self alloc] initWithSomeValue:someValue]; // no return statement
});
}
Usage in the right part of an assignment:
作业右侧部分的用法:
- (void) someMethod {
MethodPrivateHelper *helper = SHARED_INSTANCE( [[MethodPrivateHelper alloc] init] );
// do smth with the helper
}
// someMethod should not call itself to avoid deadlock, see bbum's answer
This modification utilizes two language features: the GCC compound expressionsextension, which is also supported by Clang, and the C99 variadic macros support.
此修改利用了两种语言功能:Clang 也支持的 GCC复合表达式扩展,以及 C99可变参数宏支持.
After pre-processing, the output will look like (you can test it yourself by invoking Product > Perform Action > Preprocess "YourClassName.m"in Xcode 5):
预处理后,输出将如下所示(您可以通过Product > Perform Action > Preprocess "YourClassName.m"在 Xcode 5 中调用来自行测试):
+ (instancetype) sharedInstanceOneLine {
return ({
static dispatch_once_t pred;
static id sharedObject;
dispatch_once(&pred, ^{
sharedObject = ( [[self alloc] init] );
});
sharedObject; // this object will be returned from the block
});
}
+ (instancetype) sharedInstanceMultiLine {
return ({
static dispatch_once_t pred;
static id sharedObject;
dispatch_once(&pred, ^{
sharedObject = ({
NSLog(@"creating shared instance");
CGFloat someValue = 84 / 2.0f;
[[self alloc] initWithSomeValue:someValue];
});
});
sharedObject;
});
}
- (void) someMethod {
MethodPrivateHelper *helper = ({
static dispatch_once_t pred;
static id sharedObject;
dispatch_once(&pred, ^{
sharedObject = ( [[MethodPrivateHelper alloc] init] );
});
sharedObject;
});
}

