views:

378

answers:

4

Hi!

I'm developing a Cocoa (Touch) app and there's certain data (like own device information and a list of locations) that I have to persist between different views and controllers.

I thought of storing it as instance variables in my App Delegate, but addressing the delegate is quite cumbersome (no joy typing [[[UIApplication sharedApplication] delegate] locations] everytime I want to access the locations array, and it is lot of places), so I thought of introducing some kind of alias (a la NSApp) for the delegate, but except for NSApp I haven't seen this very often in other Cocoa apps.

I also thought of going one step further and introducing aliases for my singleton classes, so instead of [State sharedState], why not rename the class to _State and make a single instance of it called State?

A: 
#define FOO [[[UIApplication sharedApplication] delegate] locations]
TokenMacGuy
Yeah, a define is a good idea. Maybe with a cast, to avoid warnings
esad
+1  A: 

I would probably write a class like LocationManager that gave a singleton with [LocationManager sharedManager] or similar. Calling through the delegate breaks encapsulation (and you're calling through 3 objects to get there). Even a NSApp style #define doesn't fix this.

Steven Canfield
Locations are just a simple array of well, `Location` objects, so wouldn't introducing `LocationManager` be a bit too much overhead? I mean, my call would then be `[[LocationManager sharedManager] locations]` which isn't really more compact.Maybe a static variable holding that array that one could access through `[Location list]` and `[Location setList:]`
esad
+1  A: 

The main purpose of using a method call to get a singleton is so the singleton can be lazily prepared. For example:

static State sharedStateInstance;

@implementation State
+ (id)sharedState {
    if (!sharedStateInstance)
        sharedStateInstance = /* Allocate instance */;
    return sharedStateInstance;
}
@end

So this means that if no code ever calls +sharedState, no resources are spent creating it.

Also, this code can be improved in one place if other needs arise in the future, such as ensuring there's one instance per thread, or a shared instance for all threads (which would then need locking code around the initialization).

jmah
+1  A: 

There are several reasons that Cocoa encourages the use of [Foo sharedFoo] rather than a global Foo object.

[Foo sharedFoo] can auto-instantiate the first time it's used.

Naming the instance with a capital letter is very confusing because it looks like a class, encouraging bugs. Consistency in naming is at the heart of good Objective-C. The compiler cannot protect you against a wide variety of mistakes because ObjC is highly dynamic. Good naming and self-discipline in consistency is what leads to bug-free Cocoa.

Parallelism:

Foo *foo = [Foo sharedFoo];
Foo *foo = [[[Foo alloc] init] autorelease];
Foo *foo = [Bar fooAtIndex:0];

All three of those may be legal in the same program. Just because there is a singleton instance doesn't mean that there aren't other instances, too. NSNotificationCenter is a good example of this. It's a singleton, but you can make additional instances (and there are reasons to do so).

A global variable can be globally modified. A sharedInstance cannot be. For instance, if State is a global variable (rather than a class), then State=nil is legal anywhere in the program. That breaks encapsulation and is an easy typo for state=nil that cannot be caught by the compiler. If State is a class, then the compiler can catch this easy error.

The many rules of Cocoa naming are there to encourage highly readable code, and to minimize bugs in a highly dynamic and loosely typed environment. Like Perl's use strict we should be very careful before giving up what little safety net we have.

Rob Napier
I agree with all you said, I just wanted to avoid too much typing every time I wanted to access the delegate. That's why I went with a #define alias
esad
If you find yourself accessing the app delegate often, you may have hung too much on it. Many of the things hung on the app delegate should be passed to other objects, or should be singletons.That said, while I don't like #define (since it makes working with the debugger more difficult), it is not always wrong as long as you name things well. For instance, I've used #define AppDelegate [[UIApplication sharedApplication] delegate]. Your FOO define would probably be ok if it were called SharedLocations. That said, Cocoa strongly encourages easy-to-read-and-understand over short-to-type.
Rob Napier