views:

146

answers:

4

What is a good way to get a reference to "singleton" objects in Objective-C? Note, specifically, I am not referring to the singleton pattern, I am referring to objects of which there are normally only one instance. This specifically applies to application models. For example, in a cooking app, I would like a class (RecipeModel) that can give me an NSArray of all the "Recipe" objects in my system.

I wouldn't want to load these from persistent storage every time -- it would make sense to load them once and then cache them in memory (in a variable on this class, in other words). If I do this, several of my classes will probably need a way to get to this instance.

What is the most flexible way to do this? Here are all the options I can think of.

  1. Make RecipeModel a real Singleton, with a +(RecipeModel *) sharedRecipeModel method
  2. Make the allRecipes NSArray accessible via a class method. Each viewController could then create a new RecipeModel and would get the same (static/global) data. +(NSArray *) allRecipes
  3. Create it in appDelegate and use the sharedApplication to get a reference to it
  4. Make sure everything is in interface builder, and use an IBOutlet to pass a copy of the class to my viewController

On other platforms (flex) I would use a DependencyInjection or MVC framework to handle this kind of wiring. I've read that objective-c doesn't "need" a DI framework because it has categories. I don't know enough to evaluate that, but categories certainly don't solve this problem.

+2  A: 

Go for 1, have a class like RecipeProvider with an allRecipes class method returning an array containing all recipes. In that method you can do the loading/caching/updating handling. To keep track of the data and status, have static variables in your RecipeProvider class (non thread safe) or directly in the allRecipes function.

+1  A: 

I'm not sure there's one right way to do this, certainly all of your options are feasible. My personal preference is to create a singleton object for my datastore, and add methods off of it to access the 'important stuff'; in this case, the list of recipes.

Ben Gottlieb
+1  A: 

I use the application delegate to store references to UIFont and UIColor instances, to create a de facto "style" for the user interface. Other controllers access these instances through the delegate. This is a good place for them, because the application delegate is essentially a singleton, already, and this is a sensible place for application-specific entities.

You can also create a RecipeModel singleton proper, if you think it is more sensible to separate this from your application delegate. This will accomplish the same goal.

Alex Reynolds
+1  A: 

I think you're over complicating this.

There is no reason to create a singleton class when all you need is just a generic collection object that all your other objects have access to.

The best way to give universal access to some object is to park it in the application delegate. In the case you suggested you would have something like:

@interface mytAppDelegate : NSObject <UIApplicationDelegate> {
   NSMutableArray *recipies;
...

Then anywhere in your app you could call:

UIApplicationDelegate *appDelegate=[[UIApplication sharedApplication] delegate];
RecipeObj *aRecipe=[appDelegate.recipes objectAtIndex:someIndexValue];

No fuss, no muss.

If you're using Core Data you can just park the NSManagedObjectContext object in the app delegate and access that directly. Core Data has enormous optimizations that make it as fast as more primitive hand rolled methods. I wouldn't bother trying to create a separate array unless you test and find that the direct Core Data is to slow. In the case you gave, it won't be.

TechZen
That is a good approach for small projects, but it doesn't scale because in large projects there will be too much junk in your app delegate, and similarly, I want a way to encapsulate data logic. So, what you suggest is what I'm leaning toward, but instead of having that array, it would have a reference to a class that loaded and served up that array.
Sean Clark Hess
Yes, you can overload the app delegate. The preferred method is to create a class that managers your data model and then park that as iVar in the app delegate. This makes the data model very transportable between projects. I use this method myself. It's especially useful if you have a compound model e.g. some data stored in Core Data and some in files.
TechZen