2011-08-02 27 views
1

好日子,朋友。Objective-C:静态字段和实现单例模式

有关对象 - 从新手:)

我想实现的OBJ-C单例设计模式再一次愚蠢的问题:

@interface SampleSingleton : NSObject { 
@private 
    static SampleSingleton* instance; 
} 
+(SampleSingleton*) getInstance; 

编译器返回错误:“预期说明符限定符在'静态'之前列出。

回答

8

请在下面找到我使用的Objective-C的代码片段,对正确的线程安全的单身实现

头文件:

/* 
* 
* Singleton interface that match Cocoa recommendation 
* @ http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32 
* extended with thread-safe pattern 
*/ 
@interface MyCustomManager : NSObject { 
} 

#pragma mark Singleton Thred-Safe Pattern 

+ (MyCustomManager *) sharedInstance; 
+ (id)allocWithZone:(NSZone *)zone; 
- (id)copyWithZone:(NSZone *)zone; 
- (id)retain; 
- (NSUInteger)retainCount; 
- (void)release; 
- (id)autorelease; 

#pragma mark - 

实现文件:

/* 
* My custom manager Class singleton implementation 
*/ 
@implementation MyCustomManager 

#pragma mark Initializers 

/* 
* specific initialize goes here 
*/ 
- (void) specificInitialize 
{ 
    // ... 
} 

/* 
* Ensure any owned object is properly released 
*/ 
- (void) dealloc 
{ 
[super dealloc]; 
} 

#pragma mark - 

#pragma mark Singleton Thred-Safe Pattern 

//- use Volatile to make sure we are not foiled by CPU caches 
static void * volatile sharedInstance = nil;             

/* 
* retrieve sharedInstance based on OSAtomicCompareAndSwapPtrBarrier that 
* acts as both a write barrier for the setting thread and a read barrier from the testing thread 
* more info @ http://stackoverflow.com/questions/145154/what-does-your-objective-c-singleton-look-like/2449664#2449664 
* and http://stackoverflow.com/questions/6915/thread-safe-lazy-contruction-of-a-singleton-in-c/6943#6943 
*/ 
+ (MyCustomManager *) sharedInstance { 
    //- check sharedInstance existenz 
    while (!sharedInstance) { 
     //- create a temporary instance of the singleton  
     id temp = [super allocWithZone:NSDefaultMallocZone()]; 
     //- The OSAtomicCompareAndSwapPtrBarrier function provided on Mac OS X 
     //- checks whether sharedInstance is NULL and only actually sets it to temp to it if it is. 
     //- This uses hardware support to really, literally only perform the swap once and tell whether it happened. 
     if(OSAtomicCompareAndSwapPtrBarrier(0x0, (void *)temp, &sharedInstance)) { 
      //- compute singleton initialize 
     MyCustomManager *singleton = (MyCustomManager *) sharedInstance; 
      [singleton specificInitialize]; 
     } 
     else { 
      //- if the swap didn't take place, delete the temporary instance 
      [temp release]; 
      temp = nil; 
     }                         
    } 
    //- return computed sharedInstance 
    return sharedInstance;                   
} 

/* 
* method to ensure that another instance is not allocated if someone tries to allocate 
* and initialize an instance of your class directly instead of using the class factory method. 
* Instead, it just returns the shared object. 
*/ 
+ (id)allocWithZone:(NSZone *)zone 
{ 
    return [[self sharedInstance] retain]; 
} 

/* 
* Implements the base protocol methods to do the appropriate things to ensure singleton  status. 
* Applies to memory-managed code, not to garbage-collected code 
*/ 
- (id)copyWithZone:(NSZone *)zone 
{ 
    return self; 
} 

/* 
* Implements the base protocol methods to do the appropriate things to ensure singleton status. 
* Applies to memory-managed code, not to garbage-collected code 
*/ 
- (id)retain 
{ 
    return self; 
} 

/* 
* Implements the base protocol methods to do the appropriate things to ensure singleton status. 
* Applies to memory-managed code, not to garbage-collected code 
*/ 
- (NSUInteger)retainCount 
{ 
    return NSUIntegerMax; //denotes an object that cannot be released 
} 

/* 
* Implements the base protocol methods to do the appropriate things to ensure singleton status. 
* Applies to memory-managed code, not to garbage-collected code 
*/ 
- (void)release 
{ 
    //do nothing 
} 

/* 
* Implements the base protocol methods to do the appropriate things to ensure singleton status. 
* Applies to memory-managed code, not to garbage-collected code 
*/ 
- (id)autorelease 
{ 
    return self; 
} 

#pragma mark - 

只是为了帮助你在Objective-C中开始并且不会迷失在你的项目结构中,你可以考虑让项目结构与你的文件系统相匹配,以便你的项目变得更大你不会迷路。

也请考虑使用适当的类命名约定,并坚持下去。

我一个为您提供矿山为样本:

  • 符合单例模式是使用管理后缀(例如MyCustomManager)命名任何类。

  • 任何静态类都使用助手后缀(例如MyCustomHelper)命名。

  • 任何专门用于控制特定进程的类都使用Controller后缀(例如MyParticularTaskConstroller)来命名。

  • 从另一个控制需要继承任何UI控件提供控制后缀(例如MyCustomDetailCell自定义UITableViewCell继承)

希望这有助于。

+0

这个解决方案是针对OS X的特定平台,因为我无法在iOS上使用它。你会如何在iOS上做同样的事情? – ArtOfWarfare

+0

@ArtOfWarfare:我在iOS 4上使用它没有任何问题,但它可能受到iOS5 ARC(自动引用计数)引入的影响。如果你使用的是ARC,你可以尝试通过看一下http://stackoverflow.com/a/6308556/734181 –

+0

来关闭这个特定的类。这个答案在iOS 6/7中仍然可以安全使用吗? –

0

静态SampleSingleton* instance;行不能进入@interface部分。大多数人把它放在上面。

Objective-c并不适用于单例模式以及其他一些语言。然而在this question中有很多不同的实现。有些人认为辛格尔顿根本不是一个很好的模式,我试图让自己放弃使用它 - 但这是我的选择。

8

您不能在类接口声明中使用静态。单身人士应在.m文件中声明为静态独立变量。我通常这样做(如果我觉得我不能避免一个单):

@interface SampleSingleton : NSObject 
{ 
@private 
} 

+(SampleSingleton*) theSingleton; 

@end 

// .m file 

@implementation SampleSingleton 

+(SampleSingleton*) theSingleton 
{ 
    static SampleSingleton* theSingleton = nil; 

    if (theSingleton == nil) 
    { 
     theSingleton = [[SampleSingleton alloc] init]; 
    } 
    return theSingleton; 
} 
+0

我不明白a = nil; if(a == nil){} – someUser

+0

@DavidOhanyan该变量在函数内声明为static。这意味着它与在函数外部声明静态变量完全相同,唯一的区别是它只在函数内部可见。它在程序启动时初始化,然后第一次通过函数获得一个对象引用,之后它不是零,所以它保持该对象引用直到程序退出。 – JeremyP

1

请看看我的问题here和尼克DeMoore精彩的答案(有很多的意见和代码修复)。拥有一个可以在IB中连接的单例(无论你在XCode 4中调用它)是非常有用的。

很酷的事情是,你可以使用同一个Singleton,并将它的一些插座连接到一个NIB,并将它的一些插座连接到另一个插座......因为它实际上是一个单例,整个事件中只能有一个实例运行时系统。工作得非常好。

注意:每次你使用辛格尔顿的人会说这是一个坏主意。

+1

我不认为单身人士是一个坏主意,我只是认为编写大量代码来强制执行它们并不是必要的,在Cocoa中查看你有[NSApplication sharedApplication],[NSNotificationCenter defaultCenter], [NSFileManager的defaultManager]这些都基本上是单身,他们不会尝试任何复杂的init方法。 –

+0

@Nathan Day,这是个好主意,但我的问题是想要将IBOutlets连接到Singelton对象。这充当了中央查询,本质上...我已经被告知(反复)我应该使用依赖注入等,但是我喜欢让方法签名免于重复出现的参数。无论如何,这已经在这里的评论广泛覆盖:http://programmers.stackexchange.com/questions/86202/what-bad-practice-do-you-do-and-why/86211#86211 –

0

这是怎么了,我通常实现一个单身方法

+(SampleSingleton *)sampleSingleton 
{ 
    static SampleSingleton * theSampleSingleton = nil; 
    if(theSampleSingleton == nil) 
     theSampleSingleton = [[SampleSingleton alloc] init]; 
    return theSampleSingleton; 
} 

,使这个线程安全的,你会做

+(SampleSingleton *)sampleSingleton 
{ 
    static SampleSingleton * theSampleSingleton = nil; 
    if(theSampleSingleton == nil) 
    { 
     @syncronise([SampleSingleton class]) 
     { 
      if(theSampleSingleton == nil) 
       theSampleSingleton = [[SampleSingleton alloc] init]; 
     } 
    } 
    return theSampleSingleton; 
} 

也是而是采用了单,你已经在形式的单的UIApplicationDelegate,你可以随时添加一个方法给你委托从你的委托中获取SampleSingleton。

另一个要考虑的问题是单身人士需要强制执行单身人士,UIApplication有一个共享应用程序来执行创建单身人士的功能,但是没有什么能真正阻止您创建新的实例。

+0

我是害怕你的同步版本被破坏。您**必须**删除无零的外部测试。其中一个原因是,优化编译器会将'theSampleSingleton'加载到第一个测试的寄存器中,并将在同步块内使用相同的寄存器副本进行第二次测试。还有其他更微妙的原因,不要使用它。谷歌为“双重检查锁定”。 – JeremyP

+0

我认为同步块会防止这种情况,谢谢,我将不得不诉诸原子功能。 –

+0

会在单例变量中添加volatile来解决问题。 –