views:

431

answers:

5

I am having some problems with NSString retaining. My problem is that on the second function (runItem) it doesn't seem to picking up on the value of the item1. No matter what I set it to, it seems that it is just set to nil. I am programming for Cocoa (desktop, versus iPhone), and I haven't had this type of problem with NSString before. I'm not sure what I'm doing to cause it, so if anyone can help me on this I would really appreciate it! My code in my AppController.h file:

@interface AppController : NSObject {
    NSString *item1;
}
@property (retain) NSString *item1;

- (IBAction)runItem:(id)sender;

@end

And AppController.m:

@synthesize item1;

- (void)awakeFromNib: {
    NSDictionary *savedFile = [NSDictionary dictionaryWithContentsOfFile:@"Users/me/Desktop/Testing.plist"];
    if (savedFile != nil) {
     item1 = [savedFile objectForKey:@"Item Title"];
     AppController *runFunction = [[AppController alloc] init];
     [runFunction runItem:self];
    }
    else {
     item1 = nil;
    }
}

- (IBAction)runItem:(id)sender 
    NSLog(@"%@", item1);
}
A: 

And what is the problem exactly ? is item1 not being retained ? Are you crashing or something ? please provide more info.

daniel
Hi daniel: Sorry for not specifying. I have updated the post with more information.
PF1
+1  A: 

You aren't retaining it, because you aren't going through the property—you're assigning directly to the instance variable. Use self.item1 = [savedFile objectForKey:@"Item Title"] to set the value using the property's synthesized setter, which will retain it.

Furthermore, you should use copy, not retain, for properties whose values conform to NSCoding, especially when those values may be mutable. Strings are such a case: You really don't want to be holding onto somebody else's mutable string when they mutate it. You want to make your own copy, so that your string's value remains fixed no matter what another object does with its strings.

Peter Hosey
Hi Peter: Thanks for your advice. I will definitely take that into consideration in the future. However, for this problem, it seems that no matter how I retain item1, it still doesn't work. I changed all instances of item1 to "self.item1" - but even after changing this, it still doesn't work. Even by calling [item1 retain], it still refused to retain.
PF1
A: 

Need more details:

  • Do you know awakeFromNib is being called?
  • Do you know savedFile is non-nil?
  • Do you know "Item Title" in savedFile is present and non-nil?

Also, simply calling item1 = @"something" will not result in it being retained as defined in your @property statement, you need to call self.item1 = @"something" for the synthesized property to be used, otherwise you're simply setting the pointer directly.

Nick Veys
Hi Nick:1. Yes, awakeFromNib is being called, and I checked again with a NSLog statement.2. I believe that I checked this by running "if (savedFile != nil)", but I could be wrong about this.3. Yes, that value exists in the file.Also, that makes sense about the self.item1 part - but even after adding this, it still doesn't work. Also, just to try it out, I called [item1 retain] to see if it would work that way and it didn't. So I am thinking that for some reason, it is simply not retaining no matter which way you call it.
PF1
+5  A: 

Umm, although you set item1 in the first AppController object (the one that is created by the application because it was associated with a NIB file), you are creating a second AppController object (directly, through the default "init" constructor), whose item1 was never set. And then you ask for that second object's item1, so of course it is nil.

Perhaps this is because you think that awakeFromNib is called whenever an object is initialized? But this is completely not true. awakeFromNib is only called for objects that are created when a NIB file is loaded.

newacct
This is correct, though there's no colon in "awakeFromNib".
Jim Puls
newacct: That makes a lot of sense. I switched it to [self runItem:self] and now it works great. Thanks for your help!
PF1
+2  A: 

Problem #1: In the example you provided, you have the following:

- (void)awakeFromNib: {

The NSNibAwaking protocol defines only awakeFromNib. It is extremely important to note the lack of a colon at the end. If this method in your code is actually getting called then it is probably due to some internal weirdism and not something you should depend on (unless you can find it documented somewhere, which I could not). My quick scan of the documentation says that awakeFromNib is the only selector that will be called.

Problem #2:

NSDictionary *savedFile = [NSDictionary dictionaryWithContentsOfFile:@"Users/me/Desktop/Testing.plist"];

I'm fairly sure that what you really meant was /Users, not Users. This probably means that savedFile is always NULL because it never loads, and therefore the following if() check will always use the else condition.

Problem #3:

item1 = [savedFile objectForKey:@"Item Title"];

If you are not using GC, this line is a problem since the object retrieved from the dictionary savedFile has not been retained. Additionally, if item1 contained a valid pointer to an object, you just leaked it by overwriting it directly. You may have meant self.item1 =.

A second problem with this line, wrt/ to the problem behavior, is that there is no guarantee that the savedFile dictionary has an object for the key @"Item Title". If it does not, it will return NULL.

Problem #4:

item1 = nil;

If item1 contained a valid pointer to an object, you just leaked it. You may have meant self.item1 = nil;.

johne
johne: I've corrected those problems, it is very good advice to remember in the future.
PF1