I need access to my core data managed object context often, and instead of getting an instance of the [[UIApplication sharedApplication] delegate] and storing it in a variable every time in every class, I was wondering if it would be ok to do this:
#interface NSObject(DelegateExtension)
- (AppDelegate*)appDelegate;
#end
#implementation
NSObject(DelegateExtension)
- (AppDelegate*)appDelegate
{
return (AppDelegate*)[[UIApplication sharedApplication] delegate];
}
#end
so then I can just do self.appDelegate anywhere in my code.
Is anything wrong with doing this that might night be obvious? Is it bad programming practice?
Alternatively, you could add a preprocessor macro (or a static C function) to your Prefix.pch file:
#define AppDelegateInstance() (AppDelegate *)[[UIApplication sharedApplication] delegate]
This will make your app delegate accessible from anywhere in your code, and there is no chance of conflicting with any existing methods named appDelegate.
I usually use a global variable to point to the app delegate.
In MyAppDelegate.h:
extern MyAppDelegate* AppDelegate;
In MyAppDelegate.m:
MyAppDelegate* AppDelegate = nil;
- (id)init {
if (self = [super init]) {
AppDelegate = self;
…
}
}
Now anyone who imports "MyAppDelegate.h" can use the AppDelegate variable to access your app delegate.
NSObject doesn't have any meaningful connection to UIApplication or its delegate. From a design standpoint, I think doing this would be a hack in the bad sense. There are three other solutions that I can come up with off the top of my head that I think would be millions of times better:
Function, whose declaration is in a header which is imported by your prefix header.
Category on UIApplication, which is actually a class that has something to do with the action you're trying to take.
Global pointer, like NSApp on OS X*, that's set up as the first thing in your program to point to the UIApplication instance.
(Four solutions!) Global pointer to the app delegate.
See On lazy instantiation and convenience methods for info on implementing 3 or 4.
*Really don't understand why they didn't do this on iOS.
Of course it's safe, so long as you know that apple doesn't or won't have a method named appDelegate in the near future. Subclassing is the preferred method, especially because NSObject is a root class. But, that would require class posing, something deprecated completely in OSX 10.5+ and never even a viable option on iOS devices.
Related
I'm just beginning to learn how to use Core Data. The tutorial that I'm learning from feels very helpful so far. In the part where they hook up the view controller where we want to store data to core data, they didn't explain the process of the method that does this "magic".
It's an implementation of the method managedObjectContext, so I thought it could be helpful if I post it here and you can explain to me/us what is the process that is being done here:
- (NSManagedObjectContext *) managedObjectContext
{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
Also for some reason when I'm returning the context object it's not in regular colour when I'm returning a class object, its just white:
Thanks a bunch!
id delegate = [[UIApplication sharedApplication] delegate];
Here, we acquire a reference to the application delegate. This is an instance of the class that XCode generated for you (which should have the name [Prefix of your project]AppDelegate).
[UIApplication sharedApplication] returns a singleton object. Singleton is a design pattern which can basically be summed up as "if you call this method, it will always return the same object." So what that means here is that we are always getting the same application delegate whenever we call this in the program.
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
I'm actually kind of at a loss for why the tutorial wants you to call performSelector: here, respondsToSelector: seems a lot more appropriate, and performSelector: will raise an exception if your delegate object isn't of the right class anyway, so there's no point in guarding the actual message with an if statement.
If you ignore what seems to me to be unnecessary boilerplate, all you're doing is calling a method on the app delegate singleton. You can look it up in your project and see what it does -- judging by the context, it returns an NSManagedObjectContext class.
return context;
Note that this is not actually returning a class object, but instead a instance of NSManagedObjectContext. Therefore, the color white is to be expected.
What that code is doing is
Looking up the app delegate object. This is probably called something like AppDelegate in your project. This code finds the singleton by asking the shared UIApplication instance for its delegate.
Checking that the app delegate has a method named managedObjectContext.
If so, calling that method, and returning the result.
This is a lousy way to do it. Much better would be to declare a property on the view controller:
#property (readwrite, strong) NSManagedObjectContext *managedObjectContext;
Then assign a value to it when creating the view controller
newViewController.managedObjectContext = self.managedObjectContext;
That is, pass the managed object context around, assigning it to whatever object needs to use it. Don't assume that you can always look it up from the app delegate, because that will break if you ever change that part of your app delegate.
Xcode uses that color because it's a local variable in that method. Whether it's an object or some other type doesn't matter.
When you build a Cocoa App, you can add all the global methods and objects in AppDelegate class, and in any place of the code, you can get access to AppDelegate via
AppDelegate* appDlgt = (AppDelegate*)[[NSApplication sharedApplication] delegate];
But how can I do the same thing in a Cocoa Framework? It doesn't seem to have AppDelegete
Thanks
Like this
+ (instancetype)sharedMyClass
{
static id kSharedMyClass = nil;
static dispatch_once_t theOnceToken;
dispatch_once( &theOnceToken, ^{ kSharedMyClass = [[self alloc] init]; } );
return kSharedMyClass;
}
NSApp is the shortcut to your NSApplication instance, which normally have a delegate, or more archaically, a subclass where you could store data.
so if you followed the default project template:
((YourAppDelegate *)[NSApp delegate]).property
will get you where you want to go
don't abuse this, as you should only really store things that belong to the application in the delegate, and even then, those user defined properties should be NSUserDefaults or contained in some other controller class.
edit: sorry, I kind of missed the point....
if you need higher level application data to be passed down into a framework, then you can do so many things, with out knowing the specifics it would be hard to comment on which would be best...
some options:
define a protocol in the framework with the properties you are interested in, the adopt that protocol in your delegate.
then ((id <FrameworkProtocol>)NSApp.delegate).protoPropertywould work.
but that introduces a requirement that the delegate adopts that protocol, which you can check with the conformsToProtocol: method.
I'm trying to access a property in my app delegate from another class (something I thought would be rather simply) but I'm having troubles in doing so. My files currently look like this:
LTAppDelegate.h
#import <Cocoa/Cocoa.h>
#import "Subject.h"
#interface LTAppDelegate : NSObject <NSApplicationDelegate, NSOutlineViewDelegate, NSOutlineViewDataSource, NSMenuDelegate> {
}
#property Subject *selectedSubject;
#end
LTAppDelegate.m
#synthesize selectedSubject;
The value for selectedSubject is then set inside applicationDidFinishLaunchingin LTAppDelegate.m. Now I'm wanting to get access to this from another class that I have, which is called LTTableViewController and is setup like so:
LTTableViewController.h
#import <Foundation/Foundation.h>
#import "LTAppDelegate.h"
#import "Subject.h"
#import "Note.h"
#interface LTTableViewController : NSObject{
NSMutableArray *notesArray;
LTAppDelegate *appDelegate;
Subject *s;
}
-(IBAction)currentSubjectDetails:(id)sender;
#end
LTTableViewController.m
#import "LTTableViewController.h"
#implementation LTTableViewController
- (id)init
{
self = [super init];
if (self) {
appDelegate = ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]);
s = [appDelegate selectedSubject];
NSLog(#"Test Subject: %#", [s title]);
}
return self;
}
-(IBAction)currentSubjectDetails:(id)sender{
NSLog(#"Selected Subject: %#", [s title]);
}
After inserting various NSLog() messages it would appear that the init method of LTTableViewController is called before applicationDidFinishLaunchingis called in LTAppDelegate. Based on that it makes sense that the "Test Subject" NSLog() in LTTableViewController.m init displays null; however, the 'currentSubjectDetails' method is linked to a button on the interface and when that is pressed after the app is finished loading, the NSLog() message still returns null.
Is there anything obvious I'm missing here. I feel like I'm being a little stupid and missing something really basic.
Similar issue is described here http://iphonedevsdk.com/forum/iphone-sdk-development/11537-viewcontroller-called-before-applicationdidfinishlaunching.html Adding this kind of functionality in the constructor is usually not recommended. Generally, I'd suggest using parameters and not relying on hidden dependencies as those will necessarily depend on the order of execution and you lose the help of the compiler to avoid invalid values. View controller initializers should not be used to store mutable references since view controllers are initialized automatically by predefined constructors, and you cannot pass parameters to them this way.
If you need to access the app delegate, then obtain it, perform operations on it and drop the reference. Try not to cache it, you'll very likely introduce hidden issues. I suggest you hook into the appear-disappear cycle if the viewed contents depend on any kind of current state.
Well, s does not exist, since it is set to null in init, so -currentSubjectDetails prints null. It is not a good idea to set your private variables in the constructor if they depend on other objects.
Rather, let the other objects explicitly tell your controller that it should use that Subject (e.g., treat s as a property).
Or, just query ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]); every time.
-applicationDidFinishLaunching called when e.g. all nib's object initialized, so launching will be ended after construction of views related stuff. This means that constructors of nib's objects wouldn't use any other nib's objects (your delegate and controller initializing with nib, right?).
Try to use -awakeFromNib instead of constructors, I think it will called after construction of both objects.
If you are trying to avoid often calls of ((LTAppDelegate *)[[NSApplication sharedApplication] delegate]) I'll recommend to pass it as method parameter, in function stack. Cyclic references defense and some flexibility.
For simplicity's sake, lets say I am making a stopwatch application for ios5. I have a class called stopwatch, which represents the stopwatch's state. This class possesses members that store elapsed time, whether the stopwatch is running, etc.
To me, it seems this class should be instantiated once at the beginning of my apps lifetime, thus I declare it to be a member of the AppDelegate class and instantiate it in the didFinishLaunchingWithOptions: method of the AppDelegate class
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
Stopwatch *stopwatch = [[Stopwatch alloc] init];
return YES;
}
However, I now find myself needing to be able to reference this stopwatch object in the ViewController class but not knowing how to do so.
I would greatly appreciate any advice as to how to approach this problem, as it seems to be a fundamental concept for making ios applications. Thank you!
Don’t use a singleton. There are plenty of reasons why singletons are a bad design decision in most cases, I wrote a blog post about some of them.
Don’t store arbitrary data in the app delegate. That’s just misusing an existing singleton to do what you want. The app delegate should be concerned with the app’s lifecycle events, it should not be a place to stick whatever data you want accessible from everywhere.
One possible correct approach to solve this problem is to have a class that will create you application’s object graph for you and pass the instances to all interested classes. My app delegate’s application did finish launching method mostly look like this:
- (BOOL) applicationDidFinishWhatever
{
Factory *factory = [[Factory alloc] init];
[self setWindow:[factory buildMainWindow]];
[window makeKeyAndSomething];
return YES;
}
And all the object creation and assembly gets done in the Factory class. The Factory will create the timer (and keep the single instance in memory, if needed) and it will pass the timer to view controllers:
- (void) buildMainWindow
{
UIWindow *window = …;
[window setRootViewController:[self buildMainController]];
return window;
}
- (void) buildMainController
{
UIViewController *controller = …;
[controller setTimer:[self buildTimer]];
return controller;
}
I have created a sample Xcode project that shows how to wire an app without singletons. It’s very simple right now, I will add some common use cases later.
You can use Singleton pattern. look here for more info What should my Objective-C singleton look like?
If you "declare it to be a member of the AppDelegate class" then you should use it as a property:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
Stopwatch *aStopwatch = [[Stopwatch alloc] init];
self.stopwatch = aStopwatch;
[aStopwatch release];
...
}
Later, anywhere in your code:
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate doSomethingWithStopwatchMethod];
someVariable = appDelegate.stopwatch.timeSinceStart;
...
Another approach would be to use a singleton (check SO for dozens of questions on the subject) or even a plain global variable (an integer holding a flag for instance) declared in main.m.
I am using a variable/propery of my application delegate as a global. (I don't want to deal with a singleton class.)
I am trying to write a #define statement in my Application Delegate class. If I type:
[UIApplication sharedApplication]
in my app delegate class, code hinting does not recognize sharedApplication. But if I type the same thing in a viewController class, "sharedApplication" pops right up.
In order to define a NSMutableDictionary in my applicationDelegate.h (or .m?), I write:
#define MyDictionary [[[UIApplication sharedApplication] delegate] stateDictionary]
Then if I try to use it in another class:
[[MyDictionary objectForKey:#"California"] largestCity];
I get an error that MyDictionary must be declared first. I am really confused about a lot of concepts here.
I'm pretty sure that someone will answer this better, but here's a quick one:
Let's say your application is called myApplication. Assign "global" property to MyApplicationDelegate, and then it will be accessible from anywhere like this:
// get reference to app delegate
MyApplicationDelegate *appDelegate = (MyApplicationDelegate *)[[UIApplication sharedApplication] delegate]
Property *myProperty = appDelegate.property;
Also, make sure that you link to MyApplicationDelegate file in header:
#import "MyApplicationDelegate.h"
There's a longer debate if using "global" objects is good design in general, but I won't go into that now.
I always add a category to UIApplication such as this:
#interface UIApplication (MyAppDelegate)
+(MyAppDelegate*)sharedMyAppDelegate;
#end
This way I do not have to worry about type casts at all. I often define and implement this category in the same file as the MyAppDelegate class itself. So this is the header I #import all over. All you can add it to your MyProject_Prefix.chp file.
Singletons are not bad, if your architecture is properly layered (And yes it is fully testable).