In Cocoa, Apple frequently makes use of the following paradigm:
[NSApplication sharedApplication]
[NSNotificationCenter defaultNotificationCenter]
[NSGraphicsContext currentContext]
[NSCalendar currentCalendar]
and so on.
They also will occasionally make use of a paradigm that I feel is far more legible when working with vast amounts of code.
NSApp //which maps to [NSApplication sharedApplication]
I'd love to be able to utilize this sort of global variable, both in my own classes, and in extensions to other classes.
MYClassInstance
NSDefaultNotificationCenter
NSCal /* or */ NSCurrentCalendar
and so on.
#define
. Simply #define NSCal [NSCalendar currentCalendar]
, but as we all know by now, macros are evil (or so they say), and it just doesn't seem like the right Cocoa way to go about this.
The only source I could find regarding NSApp
was APPKIT_EXTERN id NSApp;
, which is not exactly reusable code. Unless I'm mistaken, all this code does is define NSApp
to be an id
the world around. Unfortunately unhelpful.
In my searches, I've managed to find several leads regarding "global constants", however things like this:
extern NSString * const StringConstant;
are unfortunately limited to compile-time constants, and cannot map to the necessary class method.
I'd love to be able to roll my own NSApp
-style global variables, which map to class methods like [NSNotificationCenter defaultNotificationCenter]
. Is this possible? If so, how should I go about it?
I'm trying to implement specifically the framework singletons in the following way:
MySingletons.h
//...
extern id NSNotifCenter;
//...
MySingletons.m
//...
+(void)initialize
{
NSNotifCenter = [NSNotificationCenter defaultCenter];
}
//...
MyAppDelegate.m
//...
#import "MySingletons.h"
//...
//in applicationDidFinishLaunching:
[MySingletons initialize];
NSLog(@"%@", NSNotifCenter);
//...
However, this results in a compile-time error where the _NSNotifCenter symbol cannot be found.
I'm currently working on an Objective-C class to encapsulate some of the framework singletons I've referred to in this question. I'll add the GitHub information here when I get it up.
If you want to use that variable even outside the class, you must declared that variable as a global. Then the variable can be accessed using its name inside and outside the class and not using the instance of the class.
The set of all global variables is known as the global environment or global scope of the program. We declare a variable global by using the keyword global before a variable. All variables have the scope of the block, where they are declared and defined in. They can only be used after the point of their declaration.
A global variable is one declared at the start of the code and is accessible to all parts of the program. Since Java is object-oriented, everything is part of a class. ... A static variable can be declared, which can be available to all instances of a class.
Normally, when you create a variable inside a function, that variable is local, and can only be used inside that function. To create a global variable inside a function, you can use the global keyword.
That's funny, I just made this suggestion on another question.
You just expose the variable that holds the singleton instance as a global itself. NSApp
isn't actually mapping to a sharedApplication
call. It's a regular old pointer; it was set up during the application launch process to point to the same instance that you would get back from that call.
Just like NSApp
, you declare the variable for any file which imports the header:
extern MySingleton * MySingletonInstance;
in the header (you can use APPKIT_EXTERN
if you like; the docs indicate that it just resolves to extern
in ObjC anyways).
In the implementation file you define the variable. Usually the variable holding the shared instance is declared static
to confine its linkage to that file. If you remove the static
, the statement defines storage that is "redeclared" in the header.
Then, use it as you did before. The only caveat is that you still have to get your singleton setup method [MySingleton sharedInstance]
called before the first time you use the global in order to make sure it's initialized. -applicationDidFinishLaunching:
may be a good candidate for a place to do this.
As for creating pointers to framework singletons, you can just stash the result of [CocoaSingleton sharedInstance]
in whatever variable you like: an ivar in a class that wants to use it, a local variable, or in a global variable that you initialize very early in your program via a function you write.
The thing is, that's not guaranteed not to cause problems. Except in the case of NSApp
(or unless it's documented somewhere) there's really no guarantee that the object you get back from any given call to sharedInstance
is going to remain alive, valid, or useful past the end of your call stack.
This may just be paranoia, but I'd suggest not doing this unless you can find a guarantee somewhere that the supposed singletons you're interested in always return the same instance. Otherwise, you might suddenly end up with a dangling global pointer.
Addressing your code, the declaration in your header doesn't create a variable. You still need a definition somewhere:
// MySingletons.h
// Dear compiler, There exists a variable, NSNotifCenter, whose
// storage is elsewhere. I want to use that variable in this file.
extern id NSNotifCenter;
// MySingletons.m
// Dear compiler, please create this variable, reserving memory
// as necessary.
id NSNotifCenter;
@implementation MySingletons
// Now use the variable.
// etc.
If you're creating a singleton, you might want to glance at Apple's singleton documentation.
The existing discussion here was so intriguing that I did a little research and discovered something I'd never realized before: I can #import
a header file from my own project into the project's .pch
file (the precompiled header). This header file becomes automatically visible to all the other class files in my project with no effort on my part.
So here's an example of what I'm now doing. In the .pch
file, beneath the existing code:
#import "MyIncludes.h"
In MyIncludes.h are two kinds of thing, categories and externs (the latter in accordance with Josh's suggestion):
extern NSString* EnglishHiddenKey;
extern NSString* IndexOfCurrentTermKey;
@interface UIColor (mycats)
+ (UIColor*) myGolden;
+ (UIColor*) myPaler;
@end
In MyIncludes.m we provide definitions to satisfy all the declarations from the header file. The externs don't have to be defined from within any class:
#import "MyIncludes.h"
NSString* EnglishHiddenKey = @"englishHidden";
NSString* IndexOfCurrentTermKey = @"indexOfCurrentTerm";
@implementation UIColor (mycats)
+ (UIColor*) myGolden {
return [self colorWithRed:1.000 green:0.894 blue:0.541 alpha:.900];
}
+ (UIColor*) myPaler {
return [self colorWithRed:1.000 green:0.996 blue:0.901 alpha:1.000];
}
@end
Except for the part about using the pch
file to get magical global visibility, this is not really any different from Josh's suggestion. I'm posting it as a separate answer (rather than a mere comment) because it's long and needs formatting, and the explicit code might help someone.
(Note that there is no memory management, because I'm using ARC. The externs leak, of course, but they are supposed to leak: they need to live as long as the app runs.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With