views:

249

answers:

6

I have a class that contains a few instance methods which need to be called from another class. I know how to do that -

TimeFormatter *myTimeFormatter = [[TimeFormatter alloc] init];
[myTimeFormatter formatTime:time];

However, I don't want to have to alloc and init TimeFormatter every time I need to call one of its methods. (I need to call TimeFormatter's methods from various methods in another class).

I tried putting

TimeFormatter *myTimeFormatter = [[TimeFormatter alloc] init];

"by itself", or not in any blocks, but when I compile, I get an "initializer element is not constant" error.

Any input is greatly appreciated!

A: 

It sounds like you want to make TimeFormatter a singleton, where only one instance can be created. Objective-C doesn't make this super easy, but basically you can expose a static method that returns a pointer to TimeFormatter. This pointer will be allocated and initialized the first time in, and every time after that same pointer can be used. This question has some examples of creating a singleton in Objective-C.

Andy
A: 

You are trying to declare your variable outside the class? If to do it the way you want to do it you gotta declare it as static so

static TimeFormatter *myFormatter=...

From the name of the class though i dont see why you would wnat to keep one instance of your class... you can also do this with a singleton as described above, that is if you want to keep one instance of your class for the app as a whole.

Daniel
`static` isn't required; it just makes the variable private to TimeFormatter.m instead of visible throughout the application. The cause of the error message is, as it says, the initializer, which is the message expression after the equal sign. As the error message rightly objects, that expression is not constant, so it isn't valid as the initializer of a global variable (`static` or otherwise).
Peter Hosey
+7  A: 

You can use the singleton pattern. You can read more about it here.

Specifically, you'd do something like:

static TimeFormatter* gSharedTimeFormatter = nil;

@implementation TimeFormatter

+ (TimeFormatter*)sharedTimeFormatter {
  if (!gSharedTimeFormatter) {
 @synchronized(self) {
  if (!gSharedTimeFormatter) {
   gSharedTimeFormatter = [[TimeFormatter alloc] init];
  }
 }
  }
  return gSharedTimeFormatter;
}

...

@end

Notice that we check if the variable is null, and if it is, we take a lock, and check again. This way, we incur the locking cost only on the allocation path, which happens only once in the program. This pattern is known as double-checked locking.

Itay
Good answer. Note that if you move the entire **if** block to an `+initialize` method, you can reduce the checking overhead, since `+initialize` is called the first time a class is referenced. It's still a good idea to check though, because referencing any subclass will invoke the same parent's `+initialize` by default unless the child provides their own. If you do use this approach, it's always wise to document the automatic nature of the singleton initialization.
Quinn Taylor
Thanks Quinn - I didn't know about +initialize, but my experience in every language has taught my that relying on static constructors (which is what +initialize basically is) is a different route to go.
Itay
+2  A: 

Here's a detail example of a sharedMethod. Credit goes here

@implementation SearchData

@synthesize searchDict;
@synthesize searchArray;

- (id)init {
    if (self = [super init]) {
        NSString *path = [[NSBundle mainBundle] bundlePath];
        NSString *finalPath = [path stringByAppendingPathComponent:@"searches.plist"];
        searchDict = [[NSDictionary alloc] initWithContentsOfFile:finalPath];
        searchArray = [[searchDict allKeys] retain];
    }

    return self;
}

- (void)dealloc {
    [searchDict release];
    [searchArray release];
    [super dealloc];
}

static SearchData *sharedSingleton = NULL;

+ (SearchData *)sharedSearchData {
    @synchronized(self) {
        if (sharedSingleton == NULL)
                sharedSingleton = [[self alloc] init];
    }   
    return(sharedSingleton);
}

@end
Jordan
@ITay's answer is better than mine. I'd use his.
Jordan
A combination of both answers is required, otherwise, one might leak memory in a multithreaded environment. I'll update my answer.
Itay
Much faster typer than I. Good Answer.
IPX Ares
Actually, you're right Itay. Thanks.
Jordan
Just in case someone isn't clear about this - Jordan's answer is 100% accurate. However, you take a huge hit on the lock every time you access the singleton. My revised solution only incurs that hit the first time you try to access it and need to allocate.
Itay
+3  A: 

However, I don't want to have to alloc and init TimeFormatter every time I need to call one of its methods. (I need to call TimeFormatter's methods from various methods in another class).

I think it's worth clarifying some OOP terminology here.

The reason you need to alloc and init TimeFormatter is because your methods are instance methods. Because they're instance methods, you need an instance, and that's what alloc and init provide. Then you call your methods on (send messages to) the instance ([myTimeFormatter formatTimeString:…]).

The advantage of allowing instances is that you can keep state and settings in each instance, in instance variables, and make the latter into publicly-visible properties. Then you can deliberately have multiple instances, each having its own settings configured by whatever's using that instance.

If you don't need that functionality, you don't need to make these instance methods. You can make them class methods or even C functions, and then you don't need a TimeFormatter instance. With class methods, you send messages directly to the class ([TimeFormatter formatTimeString:…]).

And if you do want settings shared among all instances (and you don't have any state to keep), then you're right that you can just have one instance—a singleton.

The reason for that parenthesis is that shared state is bad, especially if two threads may use the time formatter concurrently. (For that matter, you could say that about settings, too. What if one thread wants seconds and the other doesn't? What if one wants 24-hour and the other wants 12-hour?) Better to have each thread use its own time formatter, so that they don't get tripped up by each other's state.

(BTW, if TimeFormatter is the actual name of your class: You are aware of NSDateFormatter, right? It does let you only format/parse the time.)

Peter Hosey
+1  A: 

A very nice, and easy, way to setup a Singleton is to use Matt Gallager's SYNTHESIZE_SINGLETON_FOR_CLASS.

Paul Robinson
That approach seems a little too heavy-handed for just having a singleton variable as part of an existing class, but good for a singleton-only class, so I think this is worthy of an upvote.
Quinn Taylor