iPhone Application Delegate variable is not being retained - objective-c

I am having a very odd issue retrieving/retaining a variable in my iPhone application delegate (AppDelegate). Initially, I can step through and see that my values are passed to logfile (the NSString variable in question), but when logfile is retrieved from another class (see the code below), it faults.
Here is my AppDelegate.h file:
#import < UIKit/UIKit.h >
#interface AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *_window;
MainViewController *_mainViewController;
NSString *logFile;
}
#property (nonatomic, retain) NSString *logFile;
#property (nonatomic, retain) ProductClass *item;
#property (nonatomic, retain) UIWindow *window;
-(void)appendToLog:(NSString *)textToLog;
#end
Here is my AppDelegate.m:
#import "AppDelegate.h"
#import "MainViewController.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize logFile;
- (void) applicationDidFinishLaunching:(UIApplication *)application {
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
_mainViewController = [[MainViewController alloc] init];
UINavigationController *_navigationController = [[UINavigationController alloc] initWithRootViewController:_mainViewController];
//Initialize the product class
[self appendToLog:#"Application loaded"];
[_window addSubview:_navigationController.view];
[_window makeKeyAndVisible];
}
-(void)appendToLog:(NSString *)textToLog {
//Append the log string
if(logFile!=nil) {
NSString *tmp = [[logFile stringByAppendingString:textToLog] stringByAppendingString:#"\n"];
logFile = tmp;
}
else {
NSString *tmp = [textToLog stringByAppendingString:#"\n"];
logFile = tmp;
}
}
#end
When I use the call (from another class):
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *s = [appDelegate logFile];
"logfile" comes back as "out of scope" so the local variable "s" is mush.
What am I doing wrong here? It isn't making sense to me.

You should replace logFile = tmp; with self.logFile = tmp; because you need to use the "self." prefix when assigning an ivar in order for the code to call the proper settor method. As it is, you're just assigning the ivar to an autoreleased object instance, instead of retaining it. The "self." prefix ensure that the code does the right thing. Without it, you're just assigning the variable without retaining it.

I would recommend prefixing logfile with self in your assignment statements in your AppDelegate. For example, self.logfile = ...

From the UIApplication class reference - UIApplication assigns and does not retain the delegate.
You need to initialize your instance of AppDelegate first.

Related

Instance method returns correct array from one view controller but null from others

This is my first post so apologies for any errors I make, but this has been frustrating me for hours now and I cannot find a solution.
I have an app that uses a UITabBarViewController that has 3 tabs: FirstViewController, SecondViewController and ThirdViewController.
I have an NSObject class (Manager) where I reach out to from my view controllers to pull information from the calendar. Which works perfectly fine when I use the FirstViewController, however, when I go to use the other view controllers it simply returns "null" but I know the instance method is being called because I put an NSLog in the instance method it returns a value, but this value isn't being passed onto view controllers two and three.
The code I am using is below.
AppDelegate.m
#import <UIKit/UIKit.h>
#import "EventManager.h"
#interface AppDelegate : UIResponder <UIApplicationDelegate>{
}
#property (strong, nonatomic) UIWindow *window;
#property (nonatomic, strong) EventManager *eventManager;
#end
AppDelegate.m
- (void)applicationDidBecomeActive:(UIApplication *)application {
self.eventManager = [[EventManager alloc] init];
}
EventManager.h
#import <Foundation/Foundation.h>
#import <EventKit/EKEventStore.h>
#interface EventManager : NSObject
#property (nonatomic, strong) EKEventStore *eventStore;
-(NSMutableArray *) fetchCalendars;
#end
EventManager.m
-(NSMutableArray *) fetchCalendars {
NSArray *EKCalendars = [self.eventStore calendarsForEntityType:EKEntityTypeEvent];
NSMutableArray *mutableCalendars = [[NSMutableArray alloc] initWithArray:EKCalendars];
NSLog(#"EKCalendars %#",EKCalendars);
return mutableCalendars;
}
FirstViewController.m
-(void)loadCalendars{
NSMutableArray *mutableCalendars = [self.appDelegate.eventManager fetchCalendars];
}
This works absolutely fine for loading the calendars.
SecondViewController.m
-(void)loadCalendars{
NSMutableArray *mutableCalendars = [self.appDelegate.eventManager fetchCalendars];
}
This returns null, however, the output from the NSLog[ NSLog(#"EKCalendars %#",EKCalendars)] gives the exact same output as when it code is ran for the First View Controller.
I can get the calendars from the altering the SecondViewController.m to read
-(void)loadCalendars{
EventManager *per= [[EventManager alloc]init];
calendarsArray = [per fetchCalendars];
}
But I just don't understand why I need to reinitialize the event manager as it is initialized in the applicationDidBecomeActive.
Thanks for any help you guy can give.
You can access the Application's AppDelegate using [UIApplication sharedApplication].delegate:
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSMutableArray *mutableCalendars = [appDelegate.eventManager fetchCalendars];

NSString works with one method but not the other

I have two different methods of trying to grab a variable from another class. The first one, which I would prefer using doesn't work - The second one, which I don't prefer does. Could someone please explain why?
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSString *someString;
}
#property (assign) IBOutlet NSWindow *window;
#property (retain, nonatomic) NSString *someString;
- (void)manualSetVariable;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "GrabFromAppDelegate.h"
#implementation AppDelegate
#synthesize someString;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
someString = #"The Variable";
NSLog(#"In AppDelegate (1): %#",someString);
GrabFromAppDelegate *getThis = [[GrabFromAppDelegate alloc] init];
getThis.varSet = someString;
}
- (void)manualSetVariable { // THIS METHOD WORKS (2)
someString = #"The Variable";
NSLog(#"In AppDelegate(2): %#",someString);
}
#end
GrabFromAppDelegate.h
#import <Foundation/Foundation.h>
#interface GrabFromAppDelegate : NSObject {
NSString *varSet;
IBOutlet NSTextField *variable;
}
#property(retain, nonatomic) NSString *varSet;
- (IBAction)showVariable:(id)sender;
- (IBAction)manuallyGrabVariable:(id)sender;
#end
GrabFromAppDelegate.m
#import "GrabFromAppDelegate.h"
#import "AppDelegate.h"
#implementation GrabFromAppDelegate
#synthesize varSet;
- (IBAction)showVariable:(id)sender {
if (varSet != NULL) {
[variable setStringValue:[NSString stringWithString:varSet]];
NSLog(#"Got String Using (1): %#",varSet);
}
}
- (IBAction)manuallyGrabVariable:(id)sender { // THIS METHOD WORKS (2)
AppDelegate *getString = [[AppDelegate alloc] init];
[getString manualSetVariable];
if ([getString someString] != NULL) {
[variable setStringValue:[NSString stringWithString:[getString someString]]];
NSLog(#"Got String Using (2): %#",[getString someString]);
}
}
#end
This is horribly wrong:
AppDelegate *getString = [[AppDelegate alloc] init];
That allocates an AppDelegate instance, but it's not [[NSApplication sharedApplication] delegate], so the getString instance will never execute the applicationDidFinishLaunching method and your someString iVar will never get set. If you'd set someString within an init function, [[AppDelegate alloc] init] would, of course, call init. But since getString isn't attached to an NSApplication instance, there's nothing delegating the applicationDidFinishLaunching method to it.
If you want to get a pointer to the application delegate, you can do:
AppDelegate *getString = [[NSApplication sharedApplication] delegate];
or, for brevity's sake:
AppDelegate *getString = [NSApp delegate];
In second method you are calling method from app delegate and value is set at that time in someString variable and you get response. However if you are setting value for a varibale in appDelegate using an instance the set value will passed to that particular instance of controller class not to all instances. Hence either you create a shared instance of that variable or call that particular instance
to get the value somestring by showVariable method.

Use of undeclared identifier in Objective C

I'm very new to Objective C and am having trouble with some very basic things.
In AppDelegate.m, I'm getting the following errors:
Use of undeclared identifier 'health'
Use of undeclared identifier 'attack'
Code (respectively):
[Troll setValue:100 forKeyPath:health];
[Troll setValue:10 forKeyPath:attack];
I'm not really sure how to declare the identifiers.
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSObject *Troll = [[NSNumber alloc]init];
[Troll setValue:100 forKeyPath:health];
[Troll setValue:10 forKeyPath:attack];
return YES;
}
#end
AppDelegate.h
#import `<UIKit/UIKit.h>`
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#end
#interface Troll : NSObject {
NSNumber *health;
NSNumber *attack;
}
#end
Keys are strings, and not something else (like dangling syntactic garbage). Furthermore, '100' and '10' are not objects. Even after this, you don't want to set the properties of the class itself but of its instances. Try
[troll setValue:[NSNumber numberWithInt:100] forKeyPath:#"health"];
[troll setValue:[NSNumber numberWithInt:10] forKeyPath:#"attack"];
instead.
The first thing to say is that you are not instantiating a Troll object, but a NSNumber... why? You would have to do Troll *troll = [[[Troll alloc] init] autorelease];
The way to set and get attributes from classes uses to be by declaring properties on the class. This way the compiler will manage the memory for you (retains and releases). Another way would be accessing directly your ivars.
However, if you want to use the setValue:forKeyPath: you have to use a NSString for the second paremeter, which is the name of the variable.
#interface Troll : NSObject
{
NSNumber *_health;
NSNumber *_attack;
}
#property (nonatomic, retain) NSNumber *health;
#property (nonatomic, retain) NSNumber *attack;
#end
#implementation Troll
#synthesize health = _health;
#synthesize attack = _attack;
- (void)dealloc
{
[_health release];
[_attack release];
[super release];
}
- (void)customMethod
{
//using properties
[self setHealth:[NSNumber numberWithInteger:100];
[self setAttack:[NSNumber numberWithInteger:5];
//accessing ivars directly - remember to release old values
[_health release];
_health = [[NSNumber numberWithInteger:100] retain];
[_attack release];
_attack = [[NSNumber numberWithInteger:5] retain];
}
#end
Good luck!
Although you are defining a class called Troll with a 'health' and 'attack', you aren't instantiating one. You probably want one of the following in your application didFinishLaunchingWithOptions:
Troll *troll = [[Troll alloc]init];
troll.health = [NSNumber numberWithInt:100];
troll.attack = [NSNumber numberWithInt:10];
OR
Troll *troll = [[Troll alloc]init];
[troll setHealth:[NSNumber numberWithInt:100]];
[troll setAttack:[NSNumber numberWithInt:10]];
These two are equivalent.

Use of undeclared identifier errors

i'm getting "use of undeclared identifier" errors in my .m file with the code below and can't seem to work it out.
NSArray *imageViews = [NSArray arrayWithObjects:img1, img2, img3, img4, img5, img6, img7, img8, img9, img10, img11, img12, img13, img14, img15, img16, img17, img18, img19, img20, img21, img22, img23, img24, img25, img26, img27, img28, img29, img30, img31, img32, img33, img34, img35, img36, img37, img38, img39, img40, nil];
In my .h file i have 40 images, all with referencing outlets:
#property (weak, nonatomic) IBOutlet UIImageView *imgX;
where X is a number from 1-40. In my .m file, the NSArray *imagesViews works fine as long as it's inside a method, but i can't declare it outside the method so that it is available to all methods. As an Objective-C novice, I don't where to go from here. I'd appreciate any help.
You don't have to initialize the array outside of a method to make it accessible from all methods. What you should do instead is declare it as a property and initialize it inside the viewDidLoad method.
In the .h file:
#property (strong, nonatomic) NSArray *imageViews;
#property (weak, nonatomic) IBOutlet UIImageView *img1;
// ...
In the .m file:
#synthesize imageViews, img1, img2, ...
// ...
- (void)viewDidLoad
{
[super viewDidLoad];
// ...
self.imageViews = [NSArray arrayWithObjects:self.img1, self.img2, ... , nil];
}
Also, note that because you have 40 image views, you should probably avoid declaring a property for each one of them. You can assign tags to them, and then retrieve them using the method viewWithTag.
In the header:
#interface MyClass : NSObject {
NSArray *imageViews;
}
#end
In the implementation:
#implementation MyClass
- (id) init
{
self = [super init];
if (self != nil) {
imageViews = [[NSArray arrayWithObjects:img1, nil] retain];
}
return self;
}
// now you can use imageViews also from other methods
- (void) dealloc
{
[imageViews release];
[super dealloc];
}
#end

After setting one NSManaged object to another it returns Null

I am trying to pass the selected object in my coredata from the rootviewcontroller to the edit view. The selected object is being passed but is then becoming null after the theObject=selectedObject is being called. Anyone know what im doing wrong?
This is in the edit.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "LearningAppDelegate.h"
#interface edit : UIViewController <UITextViewDelegate, UITableViewDelegate, UITableViewDataSource, UIActionSheetDelegate>{
UITableView *tableView;
NSManagedObject *theObject;
UITextView *messageView;
}
#property(nonatomic, retain) IBOutlet UITableView *tableView;
#property(nonatomic, retain) IBOutlet UITextView *messageView;
#property(nonatomic, retain) NSManagedObject *theObject;
-(id)initWithObject:(NSManagedObject *)selectedObject;
#end
This is in the edit.m:
-(id)initWithObject:(NSManagedObject *)selectedObject {
self = [super init];
if (nil == self) {
return nil;
}
NSLog(#"selectedObject: %#", selectedObject);
NSLog(#"selecetedObject.message: %#", [[selectedObject valueForKey:#"message"] description]);
theObject=selectedObject;
NSLog(#"theObject 1: %#", theObject);
NSLog(#"theObject.message 1: %#", [[theObject valueForKey:#"message"] description]);
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSLog(#"theObject 2: %#", theObject);
NSLog(#"theObject.message 2: %#", [[theObject valueForKey:#"message"] description]);
messageView.text=[[theObject valueForKey:#"message"] description];
[super viewDidLoad];
}
I am actually amazed that doesn't crash for you. You're assigning the variable selectedObject into the instance variable theObject without retaining it for your own use. By accessing the instance variable directly in the assignment 'theObject=selectedObject', you're bypassing the behavior granted by the #property declaration. This means that once selectedObject is finally dealloc'd, theObject will point to garbage memory.
The correct way to do this is to put theObject = [selectedObject retain]; in the -initWithObject: method and in -viewDidLoad to access it via self.theObject rather than just theObject.
In my own usage I prefer to give instance variables names different from the actual property name to avoid confusion. For example:
#interface SomeClass : NSObject
{
#private
NSManagedObject *_theObject;
}
#property (nonatomic, retain) NSManagedObject *theObject;
...
#end
#implementation SomeClass
#synthesize theObject = _theObject
...
- (void)dealloc
{
[_theObject release], _theObject = nil;
[super dealloc];
}
#end
I only have experience with Coredata on the desktop, but the problem looks like it would be with your initWithObject method. At no point do you actually insert the new object into the managed object context. You should be using this method to make new objects:
- (id)initWithEntity:(NSEntityDescription *)entity insertIntoManagedObjectContext:(NSManagedObjectContext *)context
As an example in pseudocode:
NSManagedObject *newObject = [[NSManagedObject alloc] initWithEntity:NSENTITYDESCRIPTION insertIntoManagedObjectContext:MANAGEDOBJECTCONTEXT];
[newObject setValue:#"VALUE_OF_SELECTED_OBJECT" forKey:#"APPROPRIATE_KEY"];
//REPEAT AS NECESSARY
[MANAGEDOBJECTCONTEXT save];
*Code not tested, naming conventions are ignored, etc.
The save is important. If you don't do this the object won't persist.