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.
Related
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.
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
I am getting the exception:
-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'
The offending line is:
[delegate.sharedData.dictFaves setObject:#"test" forKey:#"4"];
Delegate is initialized thus in MyViewController.m:
delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
This is how my delegate is defined in AppDelegate.h:
#import "CommonData.h"
...
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
NSString *tempFave;
CommonData *sharedData;
}
#property (strong, nonatomic) NSString *tempFave;
#property (strong, nonatomic) CommonData *sharedData;
sharedData is initialized in AppDelegate.m thus:
#import "AppDelegate.h"
...
#implementation AppDelegate
#synthesize sharedData;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
sharedData = [[CommonData alloc] init];
return YES;
}
sharedData is defined in CommonData.h:
#interface CommonData : NSObject
{
NSMutableDictionary *dictAffirms;
NSMutableDictionary *dictFaves;
}
#property (nonatomic, copy) NSMutableDictionary *dictAffirms;
#property (nonatomic, copy) NSMutableDictionary *dictFaves;
shared data implementation file CommonData.m:
#import "CommonData.h"
...
#implementation CommonData
#synthesize dictAffirms;
#synthesize dictFaves;
#end
I have declared the members of CommonData to be Mutable. Apparently that is insufficient. What else must I do in order to write to the Dictionaries inside CommonData?
I have seen this error before when trying to write to a dictionary that is filled from a plist. If you use
yourMutableDictionary = [someDataSource objectForKey:#"someKey"];
your dictionary will be immutable, even if it is declared mutable. Use instead
yourMutableDictionary = [someDataSource mutableArrayValueForKey:#"someKey"];
and your problem will go away, assuming this is in fact your problem. It might be something like:
yourMutableDictionary = [[NSDictionary alloc] init];
or
yourMutableDictionary = [NSDictionary new];
and you just are accidentally creating immutable objects, which is pretty much the same problem as above, just different.
It would be nice to see the code used to initialize the NSMutableDictionaries.
Edit: Maybe try something like this, as I'm curious as to what the results would be. Instead of using:
[delegate.sharedData.dictFaves setObject:#"test" forKey:#"4"];
try
NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithDictionary:delegate.sharedData.dictFaves];
[dict setObject:#"test" forKey:#"4"];
delegate.sharedData.dictFaves = dict;
[dict release];
You've declared the dictFaves to be mutable, but that doesn't mean you actually stored a mutable object in there. Check your initializer. You'll probably have something like the following:
dictFaves = [[NSDictionary alloc] init];
If so, you need to change that to NSMutableDictionary instead.
Your problem is that your property setter for the dictionary is declared as copy. NSMutableDictionary's copy method returns an immutable NSDictionary (in general, copy almost always returns an immutable object). So assuming you're using the standard synthesized setter, any time you set that property, you're assigning the wrong type behind the scenes. It should probably be strong instead.
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.
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.