Mac*_*uch 33 singleton multithreading memory-management objective-c thread-safety
使用哪种同步方法来确保单例仍然是单例?
+(Foo*)sharedInstance
{
@synchronized(self)
{
if (nil == _sharedInstance)
{
_sharedInstance = [[Foo alloc] init];
...
}
}
return _sharedInstance;
}
Run Code Online (Sandbox Code Playgroud)
还是使用互斥?
#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;
}
Run Code Online (Sandbox Code Playgroud)
嗯..对此有何评论?
bbu*_*bum 56
请务必阅读有关此问题/答案的讨论. 我们为什么要分开alloc和init调用以避免Objective-C中的死锁?
扩大竞争条件问题; 在真正的解决办法是你的应用程序中没有不确定初始化. 不确定或懒惰的初始化会导致行为很容易因看似无害的变化而改变 - 配置,"不相关"的代码更改等等......
最好在程序的生命周期中明确初始化已知良好点上的子系统.即拖放[MyClass sharedInstance];
到你的应用程序委托的applicationDidFinishLaunching:
方法,如果你真的需要一个子系统,在项目早期进行初始化(甚至更早移动它,如果你想成为额外的防御).
最好还是完全从该方法中移出初始化.即[MyClass initializeSharedInstance];
,+sharedInstance
如果不首先调用该方法,则asserts().
尽管我是一个方便的粉丝,25年的ObjC编程告诉我,懒惰的初始化是更多维护和重构头痛的源泉,而不是它的价值.
虽然存在下面描述的竞争条件,但是该代码不能解决下面描述的内容.几十年来我们不担心共享实例初始化器中的并发性.为繁荣留下错误的代码.
请记住,对于科林和哈拉德的正确答案,有一种非常微妙的竞争条件可能会让你陷入悲痛的世界.
也就是说,如果-init
正在分配的类恰好调用该sharedInstance
方法,它将在设置变量之前执行此操作.在这两种情况下都会导致僵局.
这是您想要分离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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,这仅适用于Mac OS X; 特别是X 10.6+和iOS 4.0+.在没有块的旧操作系统上,使用锁定或一种不基于块的操作的方法之一.
上述模式实际上并不能防止文本中描述的问题,并且在遇到问题时会导致死锁.问题是,这dispatch_once()
不是可重入的,因此,如果init
呼叫sharedInstance
,楔入城市.
Col*_*ler 38
最快的线程安全方法是使用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;
}
Run Code Online (Sandbox Code Playgroud)
Arv*_*vin 12
如果有人关心,这里有一个宏相同的东西:
/*!
* @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
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
20743 次 |
最近记录: |