2

Good day, friends.

Once again stupid question about Obj-C from newbie :)

I'm trying to implement singleton design pattern in Obj-C:

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

Compiler returns error: "expected specifier-qualifier-list before 'static'".

QuickNick
  • 1,921
  • 2
  • 15
  • 30

5 Answers5

8

You can't use static inside a class interface declaration. The singleton should be declared as a static stand alone variable in the .m file. I normally do this (if I feel I can't avoid having a singleton):

@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;
}
JeremyP
  • 84,577
  • 15
  • 123
  • 161
  • I can't understand a = nil; if(a==nil){} – someUser Jul 19 '12 at 06:56
  • @DavidOhanyan The variable is declared static inside the function. This means it is exactly the same as declaring a static variable outside the function, the only difference is that it is only visible inside the function. It gets initialised at program start up and then first time through the function it gets an object reference assigned and thereafter it is not nil so it keeps that object reference till the program exits. – JeremyP Jul 19 '12 at 10:00
8

Please find below the Objective-C code snippet I am using, for proper thread-safe singleton implementation

header file :

/*
 *
 * 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 -

implementation file :

/*
 * 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 -

Just to help you starting in objective-c and not get lost in your project structure, you can consider having the project structure matching your file system so as your project becomes bigger you won't get lost.

Also please consider using a proper class naming convention, and stick to it.

I a providing you mine as sample:

  • Any class that match singleton pattern is named using Manager suffix (E.g. MyCustomManager ).

  • Any static class is named using Helper suffix (E.g. MyCustomHelper).

  • 
Any class dedicated to control particular process is named using Controller suffix ( E.g. MyParticularTaskConstroller ).


  • Any UI control that inherit from another control needs provide control suffix ( E.g. MyCustomDetailCell inheriting from UITableViewCell )

Hope this helps.

Seb T.
  • 1,104
  • 12
  • 27
  • This solution is platform specific to OS X, it seems, as I wasn't able to use it on iOS. How would you do this same thing on iOS? – ArtOfWarfare Sep 24 '12 at 03:04
  • @ArtOfWarfare : I was using it on iOS 4 without any trouble but it might have been affected with iOS5 ARC (Automatic Reference Counting) introduction. If you are using ARC, you can probably try to disable it for this particular class by having a look at http://stackoverflow.com/a/6308556/734181 – Seb T. Sep 24 '12 at 06:50
  • Is this above answer still safe to use for iOS 6/7? – Irshad Mohamed Jan 03 '14 at 09:26
  • @Irshad Mohamed : Cannot provide you a definitive answer cause I haven't work on iOS since v4. But what is for sure, it will not be possible if you are using ARC implementation. As specified in ARC release notes https://developer.apple.com/library/mac/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226 You cannot explicitly invoke dealloc, or implement or invoke retain, release, retainCount, or autorelease – Seb T. Jan 06 '14 at 14:26
1

Please check out my question here and the wonderful answer by Nick DeMoore (with lots of comments and code fixes). Having a singleton that you can wire up in IB (well, whatever you call it in XCode 4) is really helpful.

The cool thing is that you can use the same Singleton and wire some of its outlets in one NIB, and some of its outlets in another... since it's really a singleton, there can be only one instance in the entire runtime system. Works amazingly well.

Note: Every time you use Singleton people will say it's a bad idea.

Community
  • 1
  • 1
Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421
  • 1
    I don't think singleton are such a bad idea, I just think that writing a lot of code to enforce them is not necessary most of the time, look in Cocoa you have things like [NSApplication sharedApplication], [NSNotificationCenter defaultCenter], [NSFileManager defaultManager] which are all basically singleton, and they don't try to do anything complicated with the init method. – Nathan Day Aug 05 '11 at 15:35
  • @Nathan Day, that's a great idea, but my issue is wanting to wire IBOutlets to a Singelton object. This acts as a central lookup, essentially... I've already been told (repeatedly) that I should be using dependency injection, etc., but I like to keep method signatures free of parameters that appear repeatedly. Anyway, this has all been covered extensively in the comments here: http://programmers.stackexchange.com/questions/86202/what-bad-practice-do-you-do-and-why/86211#86211 – Dan Rosenstark Aug 06 '11 at 21:39
0

The static SampleSingleton* instance; line can't go in the @interface section. Most people put it above.

Objective-c doesn't really lend itself to the singleton pattern as well as some other languages. However there are a bunch of different implementations to look at in this question.

Some people argue that Singleton is not a great pattern at all, and I'm trying to wean myself off using it - but that's my choice.

Community
  • 1
  • 1
iandotkelly
  • 9,024
  • 8
  • 48
  • 67
0

This is how I usually implement a singleton method

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

to make this thread safe you would do

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

also instead of using a singleton, you already have a singleton in the form of the UIApplicationDelegate, you could always add a method to you delegate to get your SampleSingleton from your delegate.

Another point to consider about singletons is it really necessary to enforce singletons, UIApplication has a sharedApplication which performs the function of creating a singleton, but there is nothing really stopping you from creating a new instance.

Nathan Day
  • 5,981
  • 2
  • 24
  • 40
  • I'm afraid your synchronized version is broken. You **must** remove the outer test for nil. One reason for this is that an optimising compiler will load `theSampleSingleton` into a register for the first test and will use the same register copy for the second test inside the synchronized block. There are other more subtle reasons not to use it too. Google for "double checked locking". – JeremyP Aug 05 '11 at 15:11
  • I thought the synchronisation block would protect against that, thanks, I will have to resort to atomic function. – Nathan Day Aug 05 '11 at 15:29
  • would adding volatile to the singleton variable solve the issue. – Nathan Day Aug 08 '11 at 14:50
  • Probably not because apart from the issue I mentioned there is also the issue of cache coherence for the object pointed to. i.e. even if the pointer is correct, the body of the object might not have been flushed out of the cache of the processor that ran the init code. It's best just to remove the outer test and live with the performance hit. – JeremyP Aug 08 '11 at 14:57
  • I see so your saying you would have to somehow declare the entire contents of the object volatile as well. – Nathan Day Aug 08 '11 at 15:32