views:

473

answers:

2

Been looking at this for a bit now and not understanding why this simple bit of code is throwing an error. Shortened for brevity:

NSMutableString *output;

...

@property (nonatomic, retain) NSMutableString *output;

...

@synthesize output;

...

// logs "output start" as expected
output = [NSMutableString stringWithCapacity:0];
[output appendString:@"output start"];
NSLog(@"%@", output);

...

// error happens here
// this is later on in a different method
[output appendString:@"doing roll for player"];

Can anyone spot my mistake?

+2  A: 

Change the line

output = [NSMutableString stringWithString:@"output start"]

to

[self setOutput:[NSMutableString stringWithString:@"output start"]]

(or self.output = ... if you prefer that notation).

Although you have declared a property, you are not using the setter, so you are not retaining the string.

invariant
Really? Huh, good to know. I thought when I @synthesize a variable it does that for me.
Typeoneerror
A: 

The solution did in fact have to do with retention, as indicated by user invariant. The class method:

output = [NSMutableString stringWithCapacity:0];

returns an autorelease NSMutableString. When assigned to my output property -- seemingly, even with the retain flag -- it did not retain it. The solution was to alloc it myself and not autorelease:

output = [[NSMutableString alloc] initWithCapacity:0];

Then the retain worked. Any explanation as to why would be very welcome.

Edit

Figured out why. I was accessing the instance vars directly instead of through the getter/setter that I synthesized. More info on my blog.

Typeoneerror
Rufus from AppleDevForums gave me a pretty good answer to a similiar thing:
Emil
It's extremely confusing, and the 7 or 8 things that you need to know for this to finally make sense aren't in the same places.First, take it on faith that `self.foo = bar;` is the exact same thing as (a syntax shortcut for) `[self setFoo:bar];` Namely, "invoke the method "`setFoo:bar`" on me (this object)." Now you would be asking yourself, "Huh? I did not write any "setFoo" method." True, you didn't. The @synthesize directive told the compiler to write the "setFoo" method.
Emil
And the compiler doesn't provide you with the source code of this method it wrote for you. But what does this hidden "setFoo:" method do?1. It checks to see if "bar" is already what "foo" is set to.2. If not, it sends the "release" message to "foo"3. It sets the pointer "foo" to the pointer "bar"4. It sends the "retain" message to "foo" (IF the @property directive for "foo" said to do so).
Emil
So by using `self.foo = bar`, which gets converted to `[self setFoo:bar]`, foo ends up pointing to a retained object.If you just code `foo=bar;` without using `self.foo=bar`, then the setFoo: method is NOT called and the retain is not done.
Emil
@Emil indeed. I hadn't updated this question, but I figured out the same myself awhile back: http://typeoneerror.com/blog/post/objective-c-properties-and-instance-vars-gotcha
Typeoneerror