 |
 |
Singleton classes in ObjC
|
 |
|
 |
|
Mac Elite
Join Date: Sep 2000
Location: Edmond, OK USA
Status:
Offline
|
|
Is there a simple Objective-C analogue to this Java class?
Code:
public class Singleton
{
// static member holds constant reference to singleton object
private static final Singleton _instance = new Singleton();
// Private constructor to enforce the singleton
private Singleton()
{
}
// public static accessor for getting to the singleton instance
public static Singleton instance()
{
return _instance;
}
}
I have read Apple's Learning Cocoa book's ObjC chapter, as well the primer at this link: http://developer.apple.com/documenta...eC/index.html. I still have no idea how to accomplish this. I tried this simple code:
Code:
@interface Singleton
{
+Singleton * _instance = [[Singleton alloc] init];
}
+ (Singleton *) instance;
@end
And that gave me a bunch of parse errors, and I haven't even dealt with the private constructor yet. Is this a simple thing? I remember this kind of stuff in C++ was not so easy, but I was hoping it would be better in ObjC.
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Sep 2000
Location: Tempe, AZ
Status:
Offline
|
|
Basically, you only provide a class factory method. Here's an example:
Code:
// Singleton.h
@interace Singleton: NSObject
+ (id)singletonInstance;
@end
// Singleton.m
@implementation Singleton
+ (id)singletonInstance
{
static Singleton *gSingletonInstance = nil;
if (nil == gSingletonInstance)
gSingletonInstance = [[[self class] alloc] init];
return gSingletonInstance;
}
// the rest of your class
@end
|
Geekspiff - generating spiffdiddlee software since before you began paying attention.
|
| |
|
|
|
 |
|
 |
|
Addicted to MacNN
Join Date: May 2001
Location: Cupertino, CA
Status:
Offline
|
|
If your application is multithreaded, you should also synchronize within the factory method.
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Sep 2000
Location: Edmond, OK USA
Status:
Offline
|
|
Originally posted by smeger:
Basically, you only provide a class factory method. Here's an example:
Code:
// Singleton.m
@implementation Singleton
+ (id)singletonInstance
{
static Singleton *gSingletonInstance = nil;
if (nil == gSingletonInstance)
gSingletonInstance = [[[self class] alloc] init];
return gSingletonInstance;
}
// the rest of your class
@end
Thanks for the info - that is good to know. I have three questions:
1 - Is there any way to have the initialization code for the instance happen outside of the method (to avoid threading issues). Or, at least, can I do some form of synchronization to prevent duplicates.
2 - Does this code prevent outside code from creating instances? This isn't critical, but I would like to make the class completely unambiguous as to usage by preventing initialization. Maybe I could use an undocumented initializer in the instance() method and provide a public initializer which just fails?
3 - Is there any way to prevent the objet from being released? I accidentaly did that just now and I imagine others might as well.[/
(Last edited by absmiths; Feb 5, 2004 at 01:23 PM.
)
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Sep 2000
Location: Edmond, OK USA
Status:
Offline
|
|
Originally posted by itai195:
If your application is multithreaded, you should also synchronize within the factory method.
That's my suspicion - but I don't know how to lock in Objective-C. In Java, I would do this:
Code:
public static synchronized Singleton instance()
{
}
I remember reading somewhere in the documentation about a lock class - do I need to use something like that?
|
|
|
| |
|
|
|
 |
|
 |
|
Addicted to MacNN
Join Date: May 2001
Location: Cupertino, CA
Status:
Offline
|
|
There's a discussion about this on the OmniGroup's mailing list here. Turns out that really enforcing the singleton design pattern in Cocoa can be difficult, especially if you are connecting the singleton to an NSView within IB.
If you want to avoid having to synchronize, the easiest option is to initialize your singleton instance within your class's +initialize method rather than initializing it lazily as in smeger's code.
|
|
|
| |
|
|
|
 |
|
 |
|
Addicted to MacNN
Join Date: May 2001
Location: Cupertino, CA
Status:
Offline
|
|
Originally posted by absmiths:
I remember reading somewhere in the documentation about a lock class - do I need to use something like that?
I think you can use NSLock, but there may be a way to do it with the Objective-C @synchronized() directive.
(Last edited by itai195; Feb 5, 2004 at 01:37 PM.
)
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Sep 2000
Location: Edmond, OK USA
Status:
Offline
|
|
Originally posted by itai195:
There's a discussion about this on the OmniGroup's mailing list here. Turns out that really enforcing the singleton design pattern in Cocoa can be difficult, especially if you are connecting the singleton to an NSView within IB.
That may be my option. So, if I just create a +initialize method, then that will be called when the class instance is created (meaning before the first instance is created, like a Java static initializer)?
Do I need to return self?
One more thing. MOst of that thread was over my head, and I couldn't find the basic thing I need to use initialize. Where do I stick the static variable? Do I put it inside the initialize method? If so, my instance() method can't access it, right? I don't know how to declare static variables in Objective-C.
(Last edited by absmiths; Feb 5, 2004 at 02:00 PM.
)
|
|
|
| |
|
|
|
 |
|
 |
|
Addicted to MacNN
Join Date: May 2001
Location: Cupertino, CA
Status:
Offline
|
|
Originally posted by absmiths:
That may be my option. So, if I just create a +initialize method, then that will be called when the class instance is created (meaning before the first instance is created, like a Java static initializer)?
Do I need to return self?
One more thing. MOst of that thread was over my head, and I couldn't find the basic thing I need to use initialize. Where do I stick the static variable? Do I put it inside the initialize method? If so, my instance() method can't access it, right? I don't know how to declare static variables in Objective-C.
Sorry, let me try to answer. You use initialize just like the Java static initializer I think, I haven't used Java in some time so I may be forgetting a detail. You don't have to return self, the method signature is +(void)initialize, here's the documentation. The initialize method is called when the class is loaded and initialized, you are correct. You can declare the static variable before your implementation.
Code:
// Singleton.m
static Singleton * myInstance;
@implementation Singleton
+ (void)initialize {
...
(Last edited by itai195; Feb 5, 2004 at 02:54 PM.
)
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Sep 2000
Location: Edmond, OK USA
Status:
Offline
|
|
Thanks for sticking with me, everyone. Here is what I have finally come up with. It seems to work fine, but I haven't really tested it thoroughly. It is a rough draft, and looks a little pointless, but it is an ObjC wrapper for a Java class - just meant to provide a bridge to the rest of the ObjC application without exposing any Java stuff.
Code:
//---------------------- .h file
#import <Foundation/Foundation.h>
// This is a framework, so I import the framework header
#import <Tarkin/Tarkin.h>
@interface ManagerProxy : NSObject
{
@private
// this is the object which is being proxied
Manager * _manager;
}
// do this once I figure out how to do singleton stuff
+(ManagerProxy *) instance;
// instance methods
-(Manager *)getManager;
- (void) testManager;
@end
Code:
//---------------------- .m file
#import "ManagerProxy.h"
static ManagerProxy *gSingletonInstance;
static int _instanceCount;
@implementation ManagerProxy
- (id) init
{
_instanceCount++;
if (_instanceCount > 1)
{
NSAssert(_instanceCount == 1,
@"Multiple instances of ManagerProxy created - use +instance");
}
NSLog(@"%@", @"Getting manager");
_manager = [[NSClassFromString(@"com.dental.query.Manager") alloc] init];
return self;
}
+ (ManagerProxy *) instance
{
return gSingletonInstance;
}
+ (void) initialize
{
static BOOL _initialized = NO;
if (!_initialized)
{
// create my singleton instance here
gSingletonInstance = [[[self class] alloc] init];
_initialized = YES;
}
}
- (Manager *) getManager
{
return _manager;
}
- (void) testManager
{
// stuff here
}
|
|
|
| |
|
|
|
 |
|
 |
|
Mac Elite
Join Date: Sep 2000
Location: Tempe, AZ
Status:
Offline
|
|
Originally posted by absmiths:
Thanks for the info - that is good to know. I have three questions:
1 - Is there any way to have the initialization code for the instance happen outside of the method (to avoid threading issues). Or, at least, can I do some form of synchronization to prevent duplicates.
As mentioned, you can do it in your +initialize method, or you can use NSLock (but you'd have to create the NSLock in the +initialize method, so it makes more sense to just create the singleton instance there and not worry about locking).
2 - Does this code prevent outside code from creating instances? This isn't critical, but I would like to make the class completely unambiguous as to usage by preventing initialization. Maybe I could use an undocumented initializer in the instance() method and provide a public initializer which just fails?
No. There's really no way in Cocoa of preventing a selector from being called. One thing you could do is use some "init" method with a funky name that isn't defined in your header to initialize the object. You'd call that when you create your singleton instead of calling "init". Then, set "init" up to throw an exception or to assert out.
I tend to do this a lot when using "abstract base classes" in objective-C. Since you can't really make an abstract class, I write implementations that just contain something like:
[NSException raise: NSInternalInconsistencyException format: @"-[MyClass mySelector] is abstract"];
3 - Is there any way to prevent the objet from being released? I accidentaly did that just now and I imagine others might as well.
Override -release in your object so that it does nothing (or, better, logs an error).
Code:
-(void)release
{ NSLog(@"You tried to release a singleton!!!"); }
|
Geekspiff - generating spiffdiddlee software since before you began paying attention.
|
| |
|
|
|
 |
|
 |
|
Addicted to MacNN
Join Date: May 2001
Location: Cupertino, CA
Status:
Offline
|
|
Originally posted by smeger:
No. There's really no way in Cocoa of preventing a selector from being called. One thing you could do is use some "init" method with a funky name that isn't defined in your header to initialize the object. You'd call that when you create your singleton instead of calling "init". Then, set "init" up to throw an exception or to assert out.ride -release in your object so that it does nothing (or, better, logs an error).
Or use doesNotRecognizeSelector
|
|
|
| |
|
|
|
 |
|
 |
|
Professional Poster
Join Date: Dec 2000
Location: Chicago, Illinois
Status:
Offline
|
|
Here's how I always did a Singleton and it works fine for me:
Code:
@implementation MySingle
MySingle* singleton = nil;
- (id)init
{
if(self = [super init])
{
if(singleton == nil)
singleton = [self retain]; //an extra retain
else
[self release];
}
return singleton;
}
+ (id)sharedInstance
{
if(singleton == nil)
return [[[MySingle alloc] init] autorelease];
else
return singleton;
}
@end
Matt Fahrenbacher
(Last edited by Ghoser777; Feb 5, 2004 at 08:08 PM.
)
|
|
|
| |
|
|
|
 |
 |
|
 |
|
|
|
|
|

|
|
 |
Forum Rules
|
 |
 |
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
|
HTML code is Off
|
|
|
|
|
|
 |
 |
 |
 |
|
 |
|