Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slight modifications to the .m file to avoid compile errors with latest clang compiler related to volatile #2

Open
Panajev opened this issue Sep 10, 2011 · 6 comments

Comments

@Panajev
Copy link

Panajev commented Sep 10, 2011

In order not to have Clang's compiler (LLVM clang not LLVM-GCC) complain about this singleton utility in its latest releases (Xcode 4.1 and Xcode 4.2), I have to make some small changes to the source code:

(I had to add volatile to many instances in which a reference to the singleton object was returned)

//
// SynthesizeSingleton.h
//
// Modified by Karl Stenerud starting 16/04/2010.
// - Moved the swizzle code to allocWithZone so that non-default init methods may be
// used to initialize the singleton.
// - Added "lesser" singleton which allows other instances besides sharedInstance to be created.
// - Added guard ifndef so that this file can be used in multiple library distributions.
// - Made singleton variable name class-specific so that it can be used on multiple classes
// within the same compilation module.
//
// Modified by CJ Hanson on 26/02/2010.
// This version of Matt's code uses method_setImplementaiton() to dynamically
// replace the +sharedInstance method with one that does not use @synchronized
//
// Based on code by Matt Gallagher from CocoaWithLove
//
// Created by Matt Gallagher on 20/10/08.
// Copyright 2009 Matt Gallagher. All rights reserved.
//
// Permission is given to use this source code file without charge in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//

ifndef SYNTHESIZE_SINGLETON_FOR_CLASS

import <objc/runtime.h>

pragma mark -

pragma mark Singleton

/* Synthesize Singleton For Class
*

  • Creates a singleton interface for the specified class with the following methods:
    *
  • + (MyClass*) sharedInstance;
  • + (void) purgeSharedInstance;
    *
  • Calling sharedInstance will instantiate the class and swizzle some methods to ensure
  • that only a single instance ever exists.
  • Calling purgeSharedInstance will destroy the shared instance and return the swizzled
  • methods to their former selves.
    *
  • Usage:
    *
  • MyClass.h:
  • ========================================
  • #import "SynthesizeSingleton.h"
  • @interface MyClass: SomeSuperclass
  • {
  •  ...
    
  • }
  • SYNTHESIZE_SINGLETON_FOR_CLASS_HEADER(MyClass);
  • @EnD
  • ========================================
    *
    *
  • MyClass.m:
  • ========================================
  • #import "MyClass.h"
  • // This line is optional. Use it if you've enabled GCC_WARN_UNDECLARED_SELECTOR
  • SYNTHESIZE_SINGLETON_FOR_CLASS_PROTOTYPE(MyClass);
  • SYNTHESIZE_SINGLETON_FOR_CLASS(MyClass);
  • ...
  • @EnD
  • ========================================
    *
    *
  • Note: Calling alloc manually will also initialize the singleton, so you
  • can call a more complex init routine to initialize the singleton like so:
    *
  • [[MyClass alloc] initWithParam:firstParam secondParam:secondParam];
    *
  • Just be sure to make such a call BEFORE you call "sharedInstance" in
  • your program.
    */

define SYNTHESIZE_SINGLETON_FOR_CLASS_HEADER(SS_CLASSNAME) \

\

  • (SS_CLASSNAME*) sharedInstance; \
  • (void) purgeSharedInstance;

define SYNTHESIZE_SINGLETON_FOR_CLASS_PROTOTYPE(SS_CLASSNAME) \

@interface SS_CLASSNAME (SynthesizeSingletonPrivate) \

  • (NSUInteger)retainCountDoNothing; \
  • (NSUInteger)retainCountDoSomething; \
  • (void)releaseDoNothing; \
  • (void)releaseDoSomething; \
  • (id)autoreleaseDoNothing; \
  • (id)autoreleaseDoSomething;
    @EnD

define SYNTHESIZE_SINGLETON_FOR_CLASS(SS_CLASSNAME) \


static volatile SS_CLASSNAME* _##SS_CLASSNAME##_sharedInstance = nil;
\

  • (volatile SS_CLASSNAME_) sharedInstanceNoSynch
    {
    return (volatile SS_CLASSNAME_) _##SS_CLASSNAME##_sharedInstance;
    }
    \
  • (volatile SS_CLASSNAME_) sharedInstanceSynch
    {
    @synchronized(self)
    {
    if(nil == _##SS_CLASSNAME##_sharedInstance)
    {
    _##SS_CLASSNAME##sharedInstance = [[self alloc] init];
    }
    }
    return (volatile SS_CLASSNAME
    ) _##SS_CLASSNAME##_sharedInstance;
    }
    \
  • (volatile SS_CLASSNAME_) sharedInstance
    {
    return (volatile SS_CLASSNAME_)[self sharedInstanceSynch];
    }
    \
  • (id)allocWithZone:(NSZone*) zone
    {
    @synchronized(self)
    {
    if (nil == _##SS_CLASSNAME##_sharedInstance)
    {
    _##SS_CLASSNAME##_sharedInstance = [super allocWithZone:zone];
    if(nil != _##SS_CLASSNAME##_sharedInstance)
    {
    Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceNoSynch));
    method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));
    method_setImplementation(class_getInstanceMethod(self, @selector(retainCount)), class_getMethodImplementation(self, @selector(retainCountDoNothing)));
    method_setImplementation(class_getInstanceMethod(self, @selector(release)), class_getMethodImplementation(self, @selector(releaseDoNothing)));
    method_setImplementation(class_getInstanceMethod(self, @selector(autorelease)), class_getMethodImplementation(self, @selector(autoreleaseDoNothing)));
    }
    }
    }
    return _##SS_CLASSNAME##_sharedInstance;
    }
    \
  • (void)purgeSharedInstance
    {
    @synchronized(self)
    {
    if(nil != _##SS_CLASSNAME##sharedInstance)
    {
    Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceSynch));
    method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));
    method_setImplementation(class_getInstanceMethod(self, @selector(retainCount)), class_getMethodImplementation(self, @selector(retainCountDoSomething)));
    method_setImplementation(class_getInstanceMethod(self, @selector(release)), class_getMethodImplementation(self, @selector(releaseDoSomething)));
    method_setImplementation(class_getInstanceMethod(self, @selector(autorelease)), class_getMethodImplementation(self, @selector(autoreleaseDoSomething)));
    [
    ##SS_CLASSNAME##_sharedInstance release];
    _##SS_CLASSNAME##_sharedInstance = nil;
    }
    }
    }
    \
  • (id)copyWithZone:(NSZone *)zone
    {
    return self;
    }
    \
  • (id)retain
    {
    return self;
    }
    \
  • (NSUInteger)retainCount
    {
    NSAssert1(1==0, @"SynthesizeSingleton: %@ ERROR: -(NSUInteger)retainCount method did not get swizzled.", self);
    return NSUIntegerMax;
    }
    \
  • (NSUInteger)retainCountDoNothing
    {
    return NSUIntegerMax;
    } \
  • (NSUInteger)retainCountDoSomething
    {
    return [super retainCount];
    }
    \
  • (oneway void)release
    {
    NSAssert1(1==0, @"SynthesizeSingleton: %@ ERROR: -(void)release method did not get swizzled.", self);
    }
    \
  • (void)releaseDoNothing{}
    \
  • (void)releaseDoSomething
    {
    @synchronized(self)
    {
    [super release];
    }
    }
    \
  • (id)autorelease
    {
    NSAssert1(1==0, @"SynthesizeSingleton: %@ ERROR: -(id)autorelease method did not get swizzled.", self);
    return self;
    }
    \
  • (id)autoreleaseDoNothing
    {
    return self;
    }
    \
  • (id)autoreleaseDoSomething
    {
    return [super autorelease];
    }

pragma mark -

pragma mark Lesser Singleton

/* A lesser singleton has a shared instance, but can also be instantiated on its own.
*

  • For a lesser singleton, you still use SYNTHESIZE_SINGLETON_FOR_CLASS_HEADER(),
  • but use SYNTHESIZE_LESSER_SINGLETON_FOR_CLASS() in the implementation file.
  • You must specify which creation methods are to initialize the shared instance
  • (besides "sharedInstance") via CALL_LESSER_SINGLETON_INIT_METHOD()
    *
  • Example:
    *
  • MyClass.h:
  • ========================================
  • #import "SynthesizeSingleton.h"
  • @interface MyClass: SomeSuperclass
  • {
  •  int value;
    
  •  ...
    
  • }
  • SYNTHESIZE_SINGLETON_FOR_CLASS_HEADER(MyClass);
    • (void) initSharedInstanceWithValue:(int) value;
  • - (id) initWithValue:(int) value;
    *
  • @EnD
  • ========================================
    *
    *
  • MyClass.m:
  • ========================================
  • #import "MyClass.h"
  • // This line is optional. Use it if you've enabled GCC_WARN_UNDECLARED_SELECTOR
  • SYNTHESIZE_SINGLETON_FOR_CLASS_PROTOTYPE(MyClass);
  • SYNTHESIZE_LESSER_SINGLETON_FOR_CLASS(MyClass);
    • (void) initSharedInstanceWithValue:(int) value
  • {
  •  CALL_LESSER_SINGLETON_INIT_METHOD(MyClass, initWithValue:value);
    
  • }
  • ...
  • @EnD
  • ========================================
    *
    *
  • Note: CALL_LESSER_SINGLETON_INIT_METHOD() will not work if your
  • init call contains commas. If you need commas (such as for varargs),
  • or other more complex initialization, use the PRE and POST macros:
    *
    • (void) initSharedInstanceComplex
  • {
  •  CALL_LESSER_SINGLETON_INIT_METHOD_PRE(MyClass);
    
  •  int firstNumber = [self getFirstNumberSomehow];
    
  •  _sharedInstance = [[self alloc] initWithValues:firstNumber, 2, 3, 4, -1];
    
  •  CALL_LESSER_SINGLETON_INIT_METHOD_POST(MyClass);
    
  • }
    */

define SYNTHESIZE_LESSER_SINGLETON_FOR_CLASS(SS_CLASSNAME) \


static volatile SS_CLASSNAME* _##SS_CLASSNAME##_sharedInstance = nil;
\

  • (SS_CLASSNAME_) sharedInstanceNoSynch
    {
    return (SS_CLASSNAME_) _##SS_CLASSNAME##_sharedInstance;
    }
    \
  • (SS_CLASSNAME_) sharedInstanceSynch
    {
    @synchronized(self)
    {
    if(nil == _##SS_CLASSNAME##_sharedInstance)
    {
    _##SS_CLASSNAME##sharedInstance = [[self alloc] init];
    if(
    ##SS_CLASSNAME##sharedInstance)
    {
    Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceNoSynch));
    method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));
    }
    }
    }
    return (SS_CLASSNAME
    ) _##SS_CLASSNAME##_sharedInstance;
    }
    \
  • (volatile SS_CLASSNAME_)sharedInstance
    {
    return (volatile SS_CLASSNAME_) [self sharedInstanceSynch];
    }
    \
  • (void)purgeSharedInstance
    {
    @synchronized(self)
    {
    Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceSynch));
    method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));
    [_##SS_CLASSNAME##_sharedInstance release];
    _##SS_CLASSNAME##_sharedInstance = nil;
    }
    }

define CALL_LESSER_SINGLETON_INIT_METHOD_PRE(SS_CLASSNAME) \

@synchronized(self)
{
if(nil == _##SS_CLASSNAME##_sharedInstance)
{

define CALL_LESSER_SINGLETON_INIT_METHOD_POST(SS_CLASSNAME) \

if(_##SS_CLASSNAME##_sharedInstance)
{
Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceNoSynch));
method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));
}
}
}

define CALL_LESSER_SINGLETON_INIT_METHOD(SS_CLASSNAME,INIT_CALL) \

CALL_LESSER_SINGLETON_INIT_METHOD_PRE(SS_CLASSNAME);
_##SS_CLASSNAME##_sharedInstance = [[self alloc] INIT_CALL];
CALL_LESSER_SINGLETON_INIT_METHOD_POST(SS_CLASSNAME)

endif /* SYNTHESIZE_SINGLETON_FOR_CLASS */

@averydev
Copy link

I'm having this same issue. Any updates on a fix?

@Panajev
Copy link
Author

Panajev commented Sep 23, 2011

With the latest Xcode 4.2 release a new little bug is spotted by the compiler. I have managed to get it to compile by casting to (id) the problematic returned reference, but it is not completely warning free so I do not know how safe it is. In single threaded applications you have nothing to fear, but in multithreaded applications you have to be able to use your Singletons without any worry.

The code you see above is mostly the one I am using now, except for:

1.) github wrote @EnD instead of @EnD for some reason.
2.) changing the following section:

@synchronized(self)
{
if (nil == _##SS_CLASSNAME##_sharedInstance)
{
_##SS_CLASSNAME##_sharedInstance = [super allocWithZone:zone];
if(nil != _##SS_CLASSNAME##_sharedInstance)
{
Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceNoSynch));
method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));
method_setImplementation(class_getInstanceMethod(self, @selector(retainCount)), class_getMethodImplementation(self, @selector(retainCountDoNothing)));
method_setImplementation(class_getInstanceMethod(self, @selector(release)), class_getMethodImplementation(self, @selector(releaseDoNothing)));
method_setImplementation(class_getInstanceMethod(self, @selector(autorelease)), class_getMethodImplementation(self, @selector(autoreleaseDoNothing)));
}
}
}
return _##SS_CLASSNAME##_sharedInstance;
} \

to

@synchronized(self)
{
if (nil == _##SS_CLASSNAME##_sharedInstance)
{
_##SS_CLASSNAME##_sharedInstance = [super allocWithZone:zone];
if(nil != _##SS_CLASSNAME##_sharedInstance)
{
Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceNoSynch));
method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));
method_setImplementation(class_getInstanceMethod(self, @selector(retainCount)), class_getMethodImplementation(self, @selector(retainCountDoNothing)));
method_setImplementation(class_getInstanceMethod(self, @selector(release)), class_getMethodImplementation(self, @selector(releaseDoNothing)));
method_setImplementation(class_getInstanceMethod(self, @selector(autorelease)), class_getMethodImplementation(self, @selector(autoreleaseDoNothing)));
}
}
}
return (id) _##SS_CLASSNAME##_sharedInstance;
} \

@Panajev Panajev closed this as completed Sep 23, 2011
@Panajev
Copy link
Author

Panajev commented Sep 23, 2011

With the latest Xcode 4.2 release a new little bug is spotted by the compiler. I have managed to get it to compile by casting to (id) the problematic returned reference, but it is not completely warning free so I do not know how safe it is. In single threaded applications you have nothing to fear, but in multithreaded applications you have to be able to use your Singletons without any worry.

The code you see above is mostly the one I am using now, except for:

1.) github wrote @EnD instead of @EnD for some reason.
2.) changing the following section:

@synchronized(self)
{
if (nil == _##SS_CLASSNAME##_sharedInstance)
{
_##SS_CLASSNAME##_sharedInstance = [super allocWithZone:zone];
if(nil != _##SS_CLASSNAME##_sharedInstance)
{
Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceNoSynch));
method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));
method_setImplementation(class_getInstanceMethod(self, @selector(retainCount)), class_getMethodImplementation(self, @selector(retainCountDoNothing)));
method_setImplementation(class_getInstanceMethod(self, @selector(release)), class_getMethodImplementation(self, @selector(releaseDoNothing)));
method_setImplementation(class_getInstanceMethod(self, @selector(autorelease)), class_getMethodImplementation(self, @selector(autoreleaseDoNothing)));
}
}
}
return _##SS_CLASSNAME##_sharedInstance;
} \

to

@synchronized(self)
{
if (nil == _##SS_CLASSNAME##_sharedInstance)
{
_##SS_CLASSNAME##_sharedInstance = [super allocWithZone:zone];
if(nil != _##SS_CLASSNAME##_sharedInstance)
{
Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceNoSynch));
method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));
method_setImplementation(class_getInstanceMethod(self, @selector(retainCount)), class_getMethodImplementation(self, @selector(retainCountDoNothing)));
method_setImplementation(class_getInstanceMethod(self, @selector(release)), class_getMethodImplementation(self, @selector(releaseDoNothing)));
method_setImplementation(class_getInstanceMethod(self, @selector(autorelease)), class_getMethodImplementation(self, @selector(autoreleaseDoNothing)));
}
}
}
return (id) _##SS_CLASSNAME##_sharedInstance;
} \

1 similar comment
@Panajev
Copy link
Author

Panajev commented Sep 23, 2011

With the latest Xcode 4.2 release a new little bug is spotted by the compiler. I have managed to get it to compile by casting to (id) the problematic returned reference, but it is not completely warning free so I do not know how safe it is. In single threaded applications you have nothing to fear, but in multithreaded applications you have to be able to use your Singletons without any worry.

The code you see above is mostly the one I am using now, except for:

1.) github wrote @EnD instead of @EnD for some reason.
2.) changing the following section:

@synchronized(self)
{
if (nil == _##SS_CLASSNAME##_sharedInstance)
{
_##SS_CLASSNAME##_sharedInstance = [super allocWithZone:zone];
if(nil != _##SS_CLASSNAME##_sharedInstance)
{
Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceNoSynch));
method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));
method_setImplementation(class_getInstanceMethod(self, @selector(retainCount)), class_getMethodImplementation(self, @selector(retainCountDoNothing)));
method_setImplementation(class_getInstanceMethod(self, @selector(release)), class_getMethodImplementation(self, @selector(releaseDoNothing)));
method_setImplementation(class_getInstanceMethod(self, @selector(autorelease)), class_getMethodImplementation(self, @selector(autoreleaseDoNothing)));
}
}
}
return _##SS_CLASSNAME##_sharedInstance;
} \

to

@synchronized(self)
{
if (nil == _##SS_CLASSNAME##_sharedInstance)
{
_##SS_CLASSNAME##_sharedInstance = [super allocWithZone:zone];
if(nil != _##SS_CLASSNAME##_sharedInstance)
{
Method newSharedInstanceMethod = class_getClassMethod(self, @selector(sharedInstanceNoSynch));
method_setImplementation(class_getClassMethod(self, @selector(sharedInstance)), method_getImplementation(newSharedInstanceMethod));
method_setImplementation(class_getInstanceMethod(self, @selector(retainCount)), class_getMethodImplementation(self, @selector(retainCountDoNothing)));
method_setImplementation(class_getInstanceMethod(self, @selector(release)), class_getMethodImplementation(self, @selector(releaseDoNothing)));
method_setImplementation(class_getInstanceMethod(self, @selector(autorelease)), class_getMethodImplementation(self, @selector(autoreleaseDoNothing)));
}
}
}
return (id) _##SS_CLASSNAME##_sharedInstance;
} \

@Panajev Panajev reopened this Sep 23, 2011
@luca-bernardi
Copy link

Any news about this issue? When this fix will be integrated in the master branch?

@dalezak
Copy link

dalezak commented Dec 14, 2012

Any update on this issue?

warning: casting 'volatile MySingleton *' to type 'MySingleton *' discards qualifiers [-Wincompatible-pointer-types]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants