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.
Related
Recently (reviewing some code) I stumbled upon an oddity that results in a bug in our program.
An API we are using has the following implementation (that I am going to write in Swift, even though the original code is in Objective-C)
internal class MyUUID: NSUUID { }
Which is completely useless as it always returns an empty instance.
I am going to paste the code from my playground here for explanation purposes.
For example: creating a simple NSUUID would be something like this:
let a = NSUUID()
a.description //this creates a valid uuid
While creating a MyUUID should be similar
let b = MyUUID()
b.description //it returns an instance, but is completely empty.
But it doesn't work.
Inspecting a little bit more, reveals the NSUUID initialiser creates a __NSConcreteUUID instance, while MyUUID doesn't and it doesn't matter what I try to do, it won't create an appropriate UUID.
So, my question: Is it possible to be able to create a child implementation of NSUUID?
Your evidence would appear empirically to answer your own question: it's not possible. NSUUID would appear to be a class cluster rather than a single class, which effectively prevents subclassing.
An alternative idea to Aaron's:
Implement an object that has an NSUUID rather than that is one. Implement -forwardingTargetForSelector: and return your instance of NSUUID. Consider overriding -isKindOfClass:, but ideally don't unless you have to. Then you should be able to pass your class as though it were an NSUUID to anyone that expects one without their knowing the difference.
Given that the solution depends upon the fallback mechanism built into dynamic messaging, I suspect there's no Swift equivalent; however if you define your class as Objective-C then it should be equally usable from Swift.
You could use class_setSuperclass to change the superclass of MyUUID at runtime. This approach would be illegal in Swift, due to type safety, but you could still do it in Objective-C.
Depending on your actual goals you may be able to use CFUUIDRef instead.
As requested, here's an example of the class_setSuperclass approach. Just drop this in to a new single view project.
#import <objc/runtime.h>
#interface MyUUID : NSUUID
- (void) UUIDWithHello;
#end
#implementation MyUUID
- (void) UUIDWithHello {
NSLog(#"Hello! %#", self.UUIDString);
}
#end
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Make a UUID that you want to subclass
NSUUID *uuid = [[NSUUID alloc] init];
NSLog(#"Initial UUID: %#", uuid.UUIDString);
// Ignore deprecation warnings, since class_setSuperclass is deprecated
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
// Change MyUUID to inherit from the NSUUID's hidden subclass instead of NSUUID
class_setSuperclass([MyUUID class], [uuid class]); // [uuid class] is __NSConcreteUUID
// Turn deprecation warnings back on
#pragma GCC diagnostic pop
// Make a new myUUID and print it
MyUUID *myUuid = [[MyUUID alloc] init];
[myUuid UUIDWithHello];
}
#end
Note that this is a bit dangerous. If whatever secret subclass NSUUID has additional instance variables, it will require more memory, which [MyUUID alloc] won't request. This could cause a crash later when something requests these instance variables.
To get around this, you could instead instantiate your MyUUID instance like this:
NSLog(#"Initial UUID's class: %#", NSStringFromClass(uuid.class));
Class topSecretUUIDSubclass = uuid.class; // __NSConcreteUUID
MyUUID *myUuid2 = [[topSecretUUIDSubclass alloc] init];
[myUuid2 UUIDWithHello];
object_setClass(myUuid2, [MyUUID class]);
Basically this will make myUuid2 a __NSConcreteUUID and then change it to a MyUUID. However, this will only work if MyUUID doesn't add any instance variables.
If MyUUID does need to add its own instance variables, it will need to override +alloc to provide additional memory for these instance variables, using class_createInstance().
I get caught out way too many times by creating eg an NSMutableArray* myArray property and then forgetting to assign self.myArray = [NSMutableArray array]; in -init. My app of course never complains in such cases because [self.myArray addObject:foo] is perfectly legal if self.myArray is nil, so I'm left scratching my head and going "double you tee eff".
I realise this is a long shot, but is there an lldb attribute or property specifier that would cause obj-c to ensure that properties are non-nil after completing -init?
I don't believe there is a compiler flag that will help you, however you could change the semantics of your array access so it goes through your own methods instead of directly to the NSMutableArray.
So instead of
#interface MyClass : NSObject
#property NSMutableArray *array
#end
use:
#interface MyClass : NSObject
- (void)addObject:(id)object;
- (NSUInteger)objectCount;
- (id)objectAtIndex:(NSUInteger)index;
- (void)removeObjectAtIndex:(NSUInteger)index;
#end
and you can add whatever checks you like to those array access methods (as well as lazily creating the array, of course).
However, as you can see, this could be add significant effort just to solve a small issue, and I wouldn't do this myself :)
- (NSMutableArray*) myArray
{
NSAssert(nil != _myArray);
return _myArray;
}
You can write this implementation of myArray accessor.
Also you can add in yours class method something like that:
- (void) AssertValid
{
NSAssert(nil != _myArray);
}
I am not aware of such mechanism and don't think it exists, because it would make little practical sense as there are many classes whose properties are nil most of the time by design.
It is possible to perform this check using class_copyPropertyList() from objc-runtime and then reading every property by name (for example, using valueForKey:). In simplest case, you would create a category method like [self logNilProperties] on NSObject and call it at the end of init... methods of classes that you want to check.
What you want is not possible. It's a language feature. You can't just change the behavior of the language.
Probably the best solution you can get is to introduce unit tests for the values of your properties. Or asserts:
assert(self.property != nil)
Here is a small piece of code which is used to throw an Exception.
NSException *exception=[[NSException alloc]initWithName:#"no result" reason:#"array empty" userInfo:nil];
[exception raise];
The exception can be caught in the AppDelegate file
void exceptionHandler(NSException *exception){
}
Before this method write
NSSetUncaughtExceptionHandler(&exceptionHandler);
in the - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Happy Coding :)
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 would like to be able to use bindings to keep my GUI synchronized to a dynamically loaded object, but as soon as I replace the object in question with another one of the same type the bindings break and the GUI stops updating. Here's some code to help you understand what I mean:
In my interface I have an instance variable to hold the object in question:
#interface AppDelegate : NSObject {
CustomObject *anObject; // This object has a "NSString *textValue" property
}
Then in my implementation I instantiate the object:
- (id) init {
self = [super init];
if (self != nil) {
anObject = [[CustomObject alloc] init];
}
return self;
}
In Interface Builder I have the "value" of a text field bound to "anObject.textValue".
When I call this method:
[anObject setValue:#"Changed anObject.textValue!" forKey:#"textValue"];
then the text field updates to reflect the new value.
But what I want to do is display the values from an object which is given after doing some work elsewhere in the application. So what I did was this:
- (void)setCustomObject:(CustomObject *)newObject {
anObject = newObject;
}
Now the result of this operation seems to break the bindings from the GUI to the CustomObject instance (anObject) which seems logical considering the bound object has been replaced by another instance.
What I want to know is if there is a way to keep the bindings functional with the dynamically created instance of CustomObject without having to re-bind every control programmatically through bind:toObject:forKeyPath:options: or similar which would require (to my knowledge) the use of IBOutlets to get a hold of the controls to then be able to bind them to the values in my new object (IMO this would make the bindings kind of useless in my situation). Is this the only solution or is there a better, cleaner way to deal with this?
I have read a good bunch of documents on developper.apple.com and elsewhere regarding bindings but I did not find anything which seems to talk about this particular case.
Thanks in advance for your time!
To be specific, I think the problem was that your setter method was called -setCustomObject: instead of -setAnObject:. If you made just that change I think that KVO would be invoked, and your bound textfields would be updated.
Abizern's note about it leaking (if you're not using GC) still applies though. Your setter should instead look something like:
- (void)setAnObject:(CustomObject *)newObject {
if (anObject != newObject) {
[anObject release];
anObject = [newObject retain];
}
}
Have a look at these docs on Key Value observing. This should show you how to change properties in a KVO compliant way.
Alternatively, set up anObject as a property:
#interface AppDelegate : NSObject {
CustomObject *anObject; // This object has a "NSString *textValue" property
}
#property (retain) CustomObject *anObject;
...
#end
#interface AppDelegate
#synthesize anObject;
...
#end
Then when changing the anObject instance, use property syntax.
self.anObject = newObject;
This will take care of the KVO stuff for you.
note:
Unless you have GC turned on your setCustomObject: method leaks.
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).