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).
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.
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.
I found some similar questions belonging this warning, but they are not answered or not really precisely asked.
My AppDelegate has an instance variable named AppModel* iVarModel.
The App Delegate has a declared Property and meanwhile even a separate getter for it, like:
AppDelegate.h
AppModel* iVarModel;
#property (nonatomic, retain) AppModel* iVarModel;
- (AppModel*) getAppModel;
AppDelegate.m
#synthesize iVarModel;
- (AppModel*) getAppModel {
return iVarModel;
}
In a different class
I like to access this via the singleton application object:
FarFarAwayClass.h
import "AppDelegate.h"
...
FarFarAwayClass.m
//get a pointer to the application object
UIApplication* thisApp = [UIApplication sharedApplication];
// get a pointer to the application's delegate
id<UIApplicationDelegate> theDelegateObject = [thisApp delegate];
// >(1)< access AppModel's property iVarModel
AppModel* iVarModel_byProp = [theDelegateObject iVarModel];
// >(2)< access AppModel's iVarModel via getter
AppModel* iVarModel_byGet = [theDelegateObject getAppModel];
Independently how I try to access it >(1)< or >(2)< it works, but I do get this warning:
Instance method '-iVarModel' not found (return type defaults to 'id')
Instance method '-getAppModel' not found (return type defaults to 'id')
Why does the compiler thinks those methods would not exist, even while he can use them correctly?
By the way it does make no difference if I skip the getter or the declared property I do always get this warning.
The compiler is warning you because you told the compiler this local variable 'theDelegateObject' is of type 'id'. In other words, you said it is "some NSObject that implements the UIApplicationDelegate protocol". You didn't say anything about it being an instance of your particular AppDelegate class.
If you do this instead the compiler will know what you're doing:
MyAppDelegate* theDelegateObject = (MyAppDelegate*) [thisApp delegate];
This gives the compiler the type information it needs to know that this object should respond to your own methods you wrote.
As for why this works fine as is at runtime, remember message passing is dynamic, the compiler doesn't need to bind to this method at compile time. It just dutifully writes the method call you asked for. At runtime, this works out because the application's delegate turns out to be an actual instance of your AppDelegate class.
Hope that makes sense?
I had a similar issue to this recently and discovered that (goodness knows how) my project had duplicate AppDelegate.h and AppDelegate.m files in different places. I was editing one and the compiler was using the other which meant it couldn't see the instance methods I was adding! Removing the duplicate files resolved the issue.
This error can occure if the instanciation of your class have the same name that the class itself
MyClassSample *MyClassSample = [[MyClassSample alloc] init];
To solve this you can simply modify the instanciation name ( M -> m )
MyClassSample *myClassSample = [[MyClassSample alloc] init];
I found it on my own. To let you know it and let others find the solution here it is:
As trying to be precisely I used:
FarFarAwayClass.m (1st version)
id<UIApplicationDelegate> theDelegateObject = [thisApp delegate];
Means providing the compiler with the information "I need a object here which adopts the protocol UIApplicationDelegate". But this seems to confuse the compiler. Just using id avoids the warning.
FarFarAwayClass.m (2nd version - to get rid of the warning named in original question)
id theDelegateObject = [thisApp delegate];
All warnings are gone :-)