sch*_*hwa 334 singleton design-patterns objective-c object-initializers
我的单例访问器方法通常是以下的一些变体:
static MyClass *gInstance = NULL;
+ (MyClass *)instance
{
@synchronized(self)
{
if (gInstance == NULL)
gInstance = [[self alloc] init];
}
return(gInstance);
}
Run Code Online (Sandbox Code Playgroud)
我可以做些什么来改善这个?
Rob*_*son 207
另一种选择是使用该+(void)initialize方法.从文档:
运行时
initialize在程序之前恰好一次发送给程序中的每个类,或者从程序中发送第一个消息.(因此,如果未使用该类,则永远不会调用该方法.)运行时initialize以线程安全的方式将消息发送到类.超类在其子类之前接收此消息.
所以你可以做类似于此的事情:
static MySingleton *sharedSingleton;
+ (void)initialize
{
static BOOL initialized = NO;
if(!initialized)
{
initialized = YES;
sharedSingleton = [[MySingleton alloc] init];
}
}
Run Code Online (Sandbox Code Playgroud)
Ben*_*ein 95
@interface MySingleton : NSObject
{
}
+ (MySingleton *)sharedSingleton;
@end
@implementation MySingleton
+ (MySingleton *)sharedSingleton
{
static MySingleton *sharedSingleton;
@synchronized(self)
{
if (!sharedSingleton)
sharedSingleton = [[MySingleton alloc] init];
return sharedSingleton;
}
}
@end
Run Code Online (Sandbox Code Playgroud)
Col*_*ett 59
根据我在下面的其他答案,我认为你应该这样做:
+ (id)sharedFoo
{
static dispatch_once_t once;
static MyFoo *sharedFoo;
dispatch_once(&once, ^ { sharedFoo = [[self alloc] init]; });
return sharedFoo;
}
Run Code Online (Sandbox Code Playgroud)
Lou*_*arg 58
由于Kendall发布了一个线程安全单例,试图避免锁定成本,我想我也会抛出一个:
#import <libkern/OSAtomic.h>
static void * volatile sharedInstance = nil;
+ (className *) sharedInstance {
while (!sharedInstance) {
className *temp = [[self alloc] init];
if(!OSAtomicCompareAndSwapPtrBarrier(0x0, temp, &sharedInstance)) {
[temp release];
}
}
return sharedInstance;
}
Run Code Online (Sandbox Code Playgroud)
好的,让我解释一下这是如何工作的:
快速情况:在正常执行sharedInstance中已经设置好了,所以while循环永远不会执行,只需测试变量的存在就会返回函数;
慢速情况:如果sharedInstance不存在,则使用比较和交换('CAS')分配实例并将其复制到其中;
争鸣情况:如果两个线程都试图调用sharedInstance在同一时间和 sharedInstance同一时间不存在,那么他们都将初始化为CAS是单身主义者和尝试的新实例到位.无论哪一个赢得CAS立即返回,无论哪一个失去释放它刚刚分配的实例并返回(现在设置)sharedInstance.单个OSAtomicCompareAndSwapPtrBarrier作为设置线程的写屏障和来自测试线程的读屏障.
小智 14
static MyClass *sharedInst = nil;
+ (id)sharedInstance
{
@synchronize( self ) {
if ( sharedInst == nil ) {
/* sharedInst set up in init */
[[self alloc] init];
}
}
return sharedInst;
}
- (id)init
{
if ( sharedInst != nil ) {
[NSException raise:NSInternalInconsistencyException
format:@"[%@ %@] cannot be called; use +[%@ %@] instead"],
NSStringFromClass([self class]), NSStringFromSelector(_cmd),
NSStringFromClass([self class]),
NSStringFromSelector(@selector(sharedInstance)"];
} else if ( self = [super init] ) {
sharedInst = self;
/* Whatever class specific here */
}
return sharedInst;
}
/* These probably do nothing in
a GC app. Keeps singleton
as an actual singleton in a
non CG app
*/
- (NSUInteger)retainCount
{
return NSUIntegerMax;
}
- (oneway void)release
{
}
- (id)retain
{
return sharedInst;
}
- (id)autorelease
{
return sharedInst;
}
lor*_*ean 12
编辑:此实现已被ARC废弃.请看一下如何实现与ARC兼容的Objective-C单例?为了正确实施.
我在其他答案中阅读的所有初始化实现都有一个共同的错误.
+ (void) initialize {
_instance = [[MySingletonClass alloc] init] // <----- Wrong!
}
+ (void) initialize {
if (self == [MySingletonClass class]){ // <----- Correct!
_instance = [[MySingletonClass alloc] init]
}
}
Run Code Online (Sandbox Code Playgroud)
Apple文档建议您检查初始化块中的类类型.因为子类默认调用initialize.存在一种非显而易见的情况,其中可以通过KVO间接地创建子类.如果您在另一个类中添加以下行:
[[MySingletonClass getInstance] addObserver:self forKeyPath:@"foo" options:0 context:nil]
Run Code Online (Sandbox Code Playgroud)
Objective-C将隐式创建MySingletonClass的子类,从而导致第二次触发+initialize.
您可能认为应该隐式检查init块中的重复初始化,如下所示:
- (id) init { <----- Wrong!
if (_instance != nil) {
// Some hack
}
else {
// Do stuff
}
return self;
}
Run Code Online (Sandbox Code Playgroud)
但是你会用脚射击自己; 或者更糟糕的是让另一个开发者有机会射击自己.
- (id) init { <----- Correct!
NSAssert(_instance == nil, @"Duplication initialization of singleton");
self = [super init];
if (self){
// Do stuff
}
return self;
}
Run Code Online (Sandbox Code Playgroud)
TL; DR,这是我的实现
@implementation MySingletonClass
static MySingletonClass * _instance;
+ (void) initialize {
if (self == [MySingletonClass class]){
_instance = [[MySingletonClass alloc] init];
}
}
- (id) init {
ZAssert (_instance == nil, @"Duplication initialization of singleton");
self = [super init];
if (self) {
// Initialization
}
return self;
}
+ (id) getInstance {
return _instance;
}
@end
Run Code Online (Sandbox Code Playgroud)
(用我们自己的断言宏替换ZAssert;或者只用NSAssert.)
Mat*_*ier 10
Singleton宏代码的详细解释在博客Cocoa With Love上
http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html.
我在sharedInstance上有一个有趣的变体,它是线程安全的,但在初始化后不会锁定.我还不确定是否按照要求修改了最佳答案,但我将其提交进一步讨论:
// Volatile to make sure we are not foiled by CPU caches
static volatile ALBackendRequestManager *sharedInstance;
// There's no need to call this directly, as method swizzling in sharedInstance
// means this will get called after the singleton is initialized.
+ (MySingleton *)simpleSharedInstance
{
return (MySingleton *)sharedInstance;
}
+ (MySingleton*)sharedInstance
{
@synchronized(self)
{
if (sharedInstance == nil)
{
sharedInstance = [[MySingleton alloc] init];
// Replace expensive thread-safe method
// with the simpler one that just returns the allocated instance.
SEL origSel = @selector(sharedInstance);
SEL newSel = @selector(simpleSharedInstance);
Method origMethod = class_getClassMethod(self, origSel);
Method newMethod = class_getClassMethod(self, newSel);
method_exchangeImplementations(origMethod, newMethod);
}
}
return (MySingleton *)sharedInstance;
}
Run Code Online (Sandbox Code Playgroud)
简短的回答:很棒.
答案很长:像....
static SomeSingleton *instance = NULL;
@implementation SomeSingleton
+ (id) instance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (instance == NULL){
instance = [[super allocWithZone:NULL] init];
}
});
return instance;
}
+ (id) allocWithZone:(NSZone *)paramZone {
return [[self instance] retain];
}
- (id) copyWithZone:(NSZone *)paramZone {
return self;
}
- (id) autorelease {
return self;
}
- (NSUInteger) retainCount {
return NSUIntegerMax;
}
- (id) retain {
return self;
}
@end
Run Code Online (Sandbox Code Playgroud)
请务必阅读dispatch/once.h标头以了解发生了什么.在这种情况下,标题注释比文档或手册页更适用.
小智 5
我已将单例转换为类,因此其他类可以继承单例属性.
Singleton.h:
static id sharedInstance = nil;
#define DEFINE_SHARED_INSTANCE + (id) sharedInstance { return [self sharedInstance:&sharedInstance]; } \
+ (id) allocWithZone:(NSZone *)zone { return [self allocWithZone:zone forInstance:&sharedInstance]; }
@interface Singleton : NSObject {
}
+ (id) sharedInstance;
+ (id) sharedInstance:(id*)inst;
+ (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst;
@end
Run Code Online (Sandbox Code Playgroud)
Singleton.m:
#import "Singleton.h"
@implementation Singleton
+ (id) sharedInstance {
return [self sharedInstance:&sharedInstance];
}
+ (id) sharedInstance:(id*)inst {
@synchronized(self)
{
if (*inst == nil)
*inst = [[self alloc] init];
}
return *inst;
}
+ (id) allocWithZone:(NSZone *)zone forInstance:(id*)inst {
@synchronized(self) {
if (*inst == nil) {
*inst = [super allocWithZone:zone];
return *inst; // assignment and return on first allocation
}
}
return nil; // on subsequent allocation attempts return nil
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX; // denotes an object that cannot be released
}
- (void)release {
//do nothing
}
- (id)autorelease {
return self;
}
@end
Run Code Online (Sandbox Code Playgroud)
这里有一个类的例子,你想成为单身人士.
#import "Singleton.h"
@interface SomeClass : Singleton {
}
@end
@implementation SomeClass
DEFINE_SHARED_INSTANCE;
@end
Run Code Online (Sandbox Code Playgroud)
关于Singleton类的唯一限制是它是NSObject的子类.但是大多数时候我在我的代码中使用单例,它们实际上是NSObject的子类,所以这个类真的可以让我的生活更轻松,并使代码更清晰.
| 归档时间: |
|
| 查看次数: |
182499 次 |
| 最近记录: |