views:

19076

answers:

4

I'm looking to be able to reference certain state/objects through anywhere in my application. For instance, a user logs in to their application, I need to call a web service and retrieve the users information. Then I want to be able to access this information from anywhere in the application with something like the following:

myAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
user = delegate.u;

Is setting an instance variable as a User object in the app delegate and referencing it from there when needed a poor way of going about it? I typically set it there upon the user's login.

Wanted to hear how the pros handle this one.

+11  A: 

I don't see any problem with your approach. I usually use a singleton to handle this situation:

// MyCommon.h:
@interface MyCommon
class MyCommon : NSObject
{
    int user;
};

@property(assign) int user;

+ (MyCommon *)singleton;

@end

// MyCommon.m:
@implementation MyCommon

static MyCommon * MyCommon_Singleton = nil;

+ (MyCommon *)singleton
{
    if (nil == MyCommon_Singleton)
    {
        MyCommon_Singleton = [[MyCommon_Singleton alloc] init];
    }

    return MyCommon_Singleton;
}
@end

The MyCommon singleton is then used anywhere in my application as follows:

int user = [MyCommon singleton].user;
e.James
I posted this a couple of years ago, and I have learned a few things since then. The important thing to realize about singletons is that they are no different from global variables. That doesn't necessarily mean that they should be avoided in all cases, it just means that they have the same drawbacks, especially when it comes to proper encapsulation and ease of testing. In this case, for example, it becomes very difficult to test individual classes that depend on `[MyCommon singleton]` in isolation without first setting up the global `user` value.
e.James
A good alternative to the globally-accessible singleton is to use dependency injection. You essentially create the same `MyCommon` object in your app delegate, and then pass it down to any child object that needs it, and then continue this process as far down your object hierarchy. This adds a little bit of work along the way, but it results in a much more object-oriented program which is easier to maintain and debug.
e.James
To see how the SO community feels on the matter, here's a link to a question I asked about singletons when I started to make the switch: http://stackoverflow.com/questions/474613
e.James
+2  A: 

Usually you would ask your application's controller for this information and it would be responsible for knowing how to store it/look it up in whatever data model exists. Your application's controller may or may not be the same as the applications delegate (in most simple applications, it is the same).

Jason Coco
+23  A: 

Normally, you should only connect things to the app delegate if they:

  • Were created from the same NIB file as the app delegate (i.e. static UI elements in single window interfaces)
  • Are associated with application-level event handling that passes through the app delegate (like the menu item for the Preferences Window)

For everything else, you should create a singleton which manages access to them.

Jason Coco suggested routing through the Application Controller. In my programs I normally avoid this, as I think it puts too much responsibility at the top level -- I think things should self-manage where possible and that higher level management should only be used when there is a requirement for coordination between peer-level modules.

I'm not going link my own blog but if you Google me and singletons you'll probably find a post I wrote going into more detail.

Matt Gallagher
Thanks Matt. Mind if I ask what in specific you're referring to when you say the menu item for the Preferences Window?
Coocoo4Cocoa
Would love to read your blog Matt, but it's unfortunately not up at the moment. Hope it's temporary.
Coocoo4Cocoa
Link to the blog post: http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html
Casebash
I'm disappointed by the lack of critism of the idea of using singletons throughout Cocoa code. Singletons are easy and "clean" in a certain sense (no global god class in the form of app delegate) but they do not lend themselves to testing at all well. Most seasoned TDD practitioners would recommend using DI, perhaps using constructor parameters to pass state or state objects around. I was searching for some information about how this is best achieved in Cocoa but all I find is advice telling us to use the Singleton pattern.
jkp
@jkp: Singletons can be tricky to unit test but don't interpret this as singletons are bad. Actually the reverse is true: it is a flaw of unit testing that it only excels on command pattern implementations and is cumbersome to setup state machines (like the common desktop application model) for each unit test. Using method parameters for every element of application state, just to avoid singletons, is a hideous design pattern. The proper solution is to swap in mock singletons as you would mock any other object (change the object returned by the sharedInstance method) before invoking the test.
Matt Gallagher
@Matt Gallagher: OK, so let me take you up on this. How would you advise someone to switch the singleton that is returned for a unit-test? Method swizzling? Some other way? I'm not saying DI is the best way (hence why I was searching for advice from seasoned cocoa developers) but this has a bad smell about it. Also as an aside, this approach is inherently limiting tests to run serially (lots of frameworks offer multi-threaded test-harnesses now which can be a boon with large test suites) which isn't great.
jkp
@jkp: All singleton classes have a method that returns the singleton (i.e. +sharedInstance). All you need to have is a separate +setSharedInstance method (used only by the unit tests) that you invoke in the setup of your unit test to swap in the mock object. If you plan to run this in parallel, you'll need to acquire a lock in +setSharedInstance that is released when you return the shared instance to its original value.
Matt Gallagher
Eww...aquiring a lock in set and releasing it in the accessor? That sounds like a recipe disaster. Also, most singleton implementations out there use a local static scoped to the singleton accessor itself so its not possible to do what you say without changing away from that implementation. None of this is very "clean" imho but I take your point, it is one approach.
jkp
+19  A: 

Matt is a bit too modest. His posting on the subject is one of the best I have read, and deserves a link. http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html

Brad Smith
-1 for not being a comment
Casebash