iPhone simulator app crashes when appending a string - objective-c

I'm a complete novice, so I'm probably missing something really easy, but I can't get my string appending to work. I add the 3rd character to typedDigit & it crashes - the method is called fine and typedDigit will get to 2 characters long. I think everything is declared properly in the header file. Code is -
-(IBAction)digitPressed:(UIButton *)sender {
NSString *digit = [[sender titleLabel] text]; // in this case, "0" - "9"
if (userIsInMiddleOfTyping) { // typedDigit is already at least 1 character long
typedDigit = [typedDigit stringByAppendingString:digit];
} else { // first character of typedDigit
typedDigit = digit;
userIsInMiddleOfTyping = YES;
}
}
Many thanks for any help!

Without the stack trace of the crash, it's hard to know the cause, but my guess will be that typedDigit is being autoreleased before the next call of your digitPressed function. stringByAppendingString: returns an autoreleased object, so you'll need to retain it if you want it to hand around past the next autorelease pool flush. For a direct fix, try something like...
if (userIsInMiddleOfTyping) {
typedDigit = [[[typedDigit autorelease] stringByAppendingString:digit] retain];
} else {
typedDigit = [digit retain];
...
More than this, you'll need to make sure you release typedDigit at some point after the typing is over, and you're finished with it.

You'll want to make sure digit is not NULL when trying to appending it. Also there is no mention of typedDigit's initialization, so if it is a garbage pointer or otherwise poorly-initialized, you'll crash when you try to manipulate it.

I think you probably have an ownership problem. If typeDigit is an object instance variable, you should be setting it with a setter method. In any case, you never call "retain" on the strings you want to keep around, so they are probably deallocated behind your back between method calls.

Related

using an NSString in its customized setter causes infinite threads

Firstly, I wrote my customized setter for an NSString* like this:
- (void)setDateString:(NSString *)newDateString {
self.dateString = newDateString;
NSInteger dateNumber = [dateString integerValue];
// this line causes crash
// do something here..blah blah
}
then the program stops due to infinitely many threads which does [XXX setDateString:].
After several useless tries I found this question/answer which tells me
do not use self. inside of custom accessors. access the variable directly,
so I made my code into
- (void)setDateString:(NSString *)newDateString {
//self.dateString = newDateString;
dateString = newDateString;
NSInteger dateNumber = [dateString integerValue];
// do something here..blah blah
}
then everything works like a charm!!
I am a junior developer of some objective languages, and a newbie to Objective-C.
I want to learn in details for this issue, instead of solving problems without understanding the reason.
So please provide me with some materials/website to understand more about this.
BTW, I use ARC.
Thank you all. :)
self.dateString = newDateString;
is equivalent to
[self setDateString:newDateString];
so this will cause infinite recursion because you are calling it inside -setDateString:.
See also Difference between self.ivar and ivar?.
The problem isn't actually this line:
NSInteger dateNumber = [dateString integerValue];
It is this line:
self.dateString = newDateString;
You see, that equals sign is akin to literally calling [self setDateString:newDateString] (in fact, that is what the compiler reinterprets it as) which creates an infinite loop. Naturally, commenting out that line would never have created that loop in the first place.
What happend is that you kept calling the function again and again causing a calling functions to fall in stak over flow
Whats wrong is int this line
self.dateString = newDateString;
This line is a objective c property, it has a getter and setters method, these method are automatically generated when you #synthesize it
the setter and getters method for dateString are:
- (void)setDateString:(NSString *)newDateString;
and
- (NSString*)dateString;
So when you call self.dateString = newDateString;
This line will call this function again
- (void)setDateString:(NSString *)newDateString {
And this function again contains the self.dateString = newDateString;, your call stack will get deeper and deeper till you are out of stack storage

Mysterious release of an NSDictionary in Objective-C?

I'm working on the finishing touches of a custom patch for Quartz Composer. As of right now, I have almost everything stripped out of the patch, and it's crashing telling me BAD ACCESS when I try to NSLog a NSDictionary value that is an ivar, and that worked perfectly in the last execution when I assigned it.
My code looks like this:
- (BOOL) startExecution:(id<QCPlugInContext>)context
{
lastBoutData = [[NSDictionary alloc] init ];
return YES;
}
- (BOOL) execute:(id<QCPlugInContext>)context atTime:(NSTimeInterval)time withArguments:(NSDictionary*)arguments
{
NSLog(#"self.inputBoutData: %#", self.inputBoutData);
NSLog(#"lastBoutData: %#", lastBoutData);
// have new data, put it on the output
self.lastBoutData = [NSDictionary dictionaryWithDictionary:self.inputBoutData];
NSLog(#"assigned: %#", lastBoutData);
return YES;
}
I can see the log shows that all three NSLog lines work perfectly until self.inputBoutData has input. Then, I see that self.inputBoutData is successfully copied to lastBoutData in the last NSLog line of the loop.
In the very next run of execute:atTime:withArguments:, self.inputBoutData is still full, but lastBoutData is blank again!!! I'm can't see how that can happen. Then, it runs one more loop, just like the last, and successfully copies the self.inputBoutData to lastBoutData, and it's logged again. The next time through, I get BAD ACCESS just before the second NSLog statement.
I was getting some error messages that told me that lastBoutData wasn't an NSDictionary, so out of desperation, I added a [lastBoutData retain], and it doesn't crash. I'm not releasing this ivar, so I'm not sure why I have to retain it. I do very similar things with other ivars in many other patches with no issues. What could I be missing? Why is this thing releasing on me, or is that even what is happening?
self.lastBoutData = [NSDictionary dictionaryWithDictionary:self.inputBoutData];
dictionaryWithDictionary: returns an autoreleased dictionary. And since your property was not (retain) nothing was retaining it. So your previous dictionary is de-referenced and leaked and your new dictionary is not retained.
consider:
[lastBoutData release];
lastBoutData = [[NSDictionary alloc] initWithDictionary:self.inputBoutData];
Or use your code as is and add retain to the property.

Why do I get EXC_BAD_ACCESS?

I have following code, which executes on button press. At first it works as expected but second time onwards application hangs and I get EXC_BAD_ACCESS signal.
- (IBAction) comicDetailsPressed:(id)sender {
static IssueProperties *props = nil;
if (props == nil) {
props = [ComicDataParser
parseComicForUrl:#"http://dummy.com/Jan.xml"];
}
NSLog(#"%d", [props totalPages]);
totalPages.text = [NSString stringWithFormat:#"%d", [props totalPages]];
}
You didn't say what line it's crashing on, which will mean answers will have to be speculative.
You have a static pointer to a IssueProperties object, but when you assign to it, you aren't using retain. You probably should.
This is assuming that the return value from parseComicForUrl: is a IssueProperties object or a subclass.
I'm assuming that the text property is an NSString set to copy and not retain. If not, it should be.
You need to retain the object you get back from +parseComicForUrl:. Also, why don't you use an instance variable for props?
Without a lot more context, it is going to be impossible to answer for sure, but my first thought would be this:
your static IssueProperties *props would not be nil the second time around. Instead, it would have the value that [ComicDataParser parseComicForUrl] returned.
My guess is that the ComicDataParser is autoreleaseing the response, and so the second time around you have a pointer that is not nil, but is now pointing to an already released object, which is invalid.
If I am right, you need a retain somewhere.

If I want to make a new instance of an object in a function whose pointer is passed by reference in it

- (void)createAString:(NSString **)str
{
*str = [NSString stringWithString:#"Hi all!"];
[*str autorelease]; // ???? is this right ?
}
How should I use release or autorelease ? I don't want to release outside of the function of course :)
...
NSString *createStr;
[self createAString:&createStr];
NSLog(#"%#", createStr);
You're correct that you'd generally want to return autoreleased (or the like) objects from out params when you use this form. Your assignment statement in the function that sets *str to a string:
*str = [NSString stringWithString:#"foo"];
is already doing the right thing, because that method returns an instance of NSString that the caller doesn't own. Just like you could return this string object from your function without any further memory management, you can set it as the outparam as you've done. Your second snippet showing the call site is fine.
This said, I'm worried about a few things in your code that you should be sure you understand:
The value of str inside the method is still a **, and sending that a message (as you've done for the speculative autorelease) is nonsense. Be sure you fully understand doubly indirected pointers before using them too liberally. :) If you need to send str a message after creating it, send it to *str, which is what contains the NSString *.
Setting an outparam like this when the function returns void is not idiomatic Cocoa. You would normally just return the NSString * directly. Outparams are rare in Cocoa. (Usually just NSErrors get this treatment from framework calls. Otherwise they conventionally use name like getString to differentiate them from normal get accessors which don't use the word "get".)
I hope -stringWithString was just an example. That method is almost never used in practice, since it's equivalent (in this case) to just using a #"string literal" (although that would muddy your example).
Instead of using a double pointer, would it not be more elegant to use an NSMutableString instead?
- (void)createAString:(NSMutableString *)str
{
[str setString:#"Hi all!"];
}
....
NSMutableString *createStr = [[NSMutableString alloc] init];
[self createAString: createStr];
NSLog(#"%#", createStr);
[createStr release];
Or, even better, just have the createAString method return an NSString.
- (NSString *)createAString
{
return #"Hi all!"; // this is autoreleased automatically
}
I wouldn't want to presume that your needs are this simple, though. =)

Is if (variable) the same as if (variable != nil) in Objective-C

I am getting a EXC_BAD_ACCESS (SIGBUS) on this line in my iPhone project:
if (timeoutTimer) [timeoutTimer invalidate];
The thing that has me stumped is that I don't understand how that line could crash, since the if statement is meant to be checking for nil. Am I misunderstanding the way Objective-C works, or do line numbers in crash statements sometime have the wrong line in them?
Just because a variable is set to a value other than nil doesn't mean it's pointing to a valid object. For example:
id object = [[NSObject alloc] init];
[object release];
NSLog(#"%#", object); // Not nil, but a deallocated object,
// meaning a likely crash
Your timer has probably already been gotten rid of (or possibly hasn't been created at all?) but the variable wasn't set to nil.
I just ran into a similar issue, so here's another example of what might cause a check such as yours to fail.
In my case, I was getting the value from a dictionary like this:
NSString *text = [dict objectForKey:#"text"];
Later on, I was using the variable like this:
if (text) {
// do something with "text"
}
This resulted in a EXC_BAD_ACCESS error and program crash.
The problem was that my dictionary used NSNull values in cases where an object had an empty value (it had been deserialized from JSON), since NSDictionary cannot hold nil values. I ended up working around it like this:
NSString *text = [dict objectForKey:#"text"];
if ([[NSNull null] isEqual:text]) {
text = nil;
}
They should be the same. Perhaps the line number is in fact incorrect.
Look for other possible errors near that in your code and see if you find anything.