NSString contains a number, why does it crash my App? - iphone

I'm having some difficulty with NSString in my application. Basically, I have an NSString called o1string which contains the value "602". I want to output this in a UIAlertView alongside some other text.
votedmessage = [ NSString stringWithFormat:#"The current standings are as follows:\n\n%#: %# votes", b1title, o1string ];
UIAlertView *votedAlert = [[UIAlertView alloc] initWithTitle:#"Thank you for voting" message:votedmessage delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
I have used NSLog and verified that the value inside the NSString is definitely 602, and the other variable (b1title) used in the message outputs fine on its own. I cannot work out why the app is crashing when I add the o1votes variable to the alert message though, is it something to do with a conflict in holding just a number inside an NSString?
This is how o1string is set. It definitely contains "602", grabbed from an XML file.
o1string = [[options objectAtIndex:3] objectForKey: #"votes"];
o1string = [o1string stringByReplacingOccurrencesOfString:#"\n" withString:#""];
o1string = [o1string stringByReplacingOccurrencesOfString:#" " withString:#""];

Unless that assignment of o1string is in the same method where votedmessage is created (since you don't say, I'm assuming not), it will be gone by the time you get to the code where votedmessage needs it.
Unless you're using garbage collection, you need to retain objects that you want to keep around past the current method. See the Objective-C memory management guide for complete details.

You need to post more code. In particular it's not clear whether the two pieces you posted are in the same function or different places.
If they're in different places you must call [o1string retain] (and later [o1string release]). The easiest way to do this would be to make olstring a property with retain semantics.
stringByReplacingOccurrencesOfString returns a temporary instance that will be auto-released sometime after the function exists.
I would guess the reason b1Title works is that it's stored in your dictionary so is persistent. o1string is created from the stringByXXX functions and is temporary.

Related

iOS pathForResource show file name only

I am implementing an audio player in my app. and I have a UITableView in which i show my list of audio tracks.
the list of audio is added into an array with
fileArray = [[NSMutableArray alloc] initWithObjects:
[[NSBundle mainBundle] pathForResource:#"A Million Years" ofType:#"mp3"],
[[NSBundle mainBundle] pathForResource:#"2 Million Years" ofType:#"mp3"],...
and in my cellForRowAtIndexPath I am doing
cell.textLabel.text = [[NSString stringWithFormat:#"%#", [self.fileArray objectAtIndex:indexPath.row]] stringByDeletingPathExtension];
but the problem is that it still shows me an entire extension "/Users/....". i only want the file name ("A Million Years", "2 Million Years") to be shown.
What am I doing wrong? I have been looking around for an answer but was never able to get it (perhaps due to wrong phrasing)
Thanks very much everybody. :)
You should use lastPathComponent if you want the last part of it. So:
cell.textLabel.text = [[[self.fileArray objectAtIndex:indexPath.row] lastPathComponent] stringByDeletingPathExtension];
That is not the extension. It's called the 'path'. (It would be good to at least get yourself to an acceptable level of knowledge in using a computing device before trying to program it...)
I suggest you use
[somePath lastPathComponent]
instead in order to get the filename only without the containing directories.
Also, the call to [NSString stringWithFormat:] is completely superfluous, wastes memory and processor time (which is unacceptable, especially on an embedded device).

Getting length of NSMutableData

I have managed to NSInputStream and read some data to NSMutableData object. I am able to put this data into string and NSLog it, however when I try to access its length(I am assuming this is its size in bytes) my app crashes.
NSString *stringData=[[NSString alloc]initWithData:self.data encoding:NSUTF8StringEncoding];
NSLog(#"%# thats data",stringData);//logs out content of data
NSLog(#"%# thats data length",[self.data length]);//crashes
So my question is if I call copy on NSMutableDate do I get immutable copy ?
Am I tying to access the length in a wrong manner ?
It's because you are trying to log the length as an object using %#. It's not an object, it's an integer, so log it with %i instead:
NSLog(#"%i thats data length",[self.data length]);
Logging an object with %# tries to call the [... description] method on whatever is passed in. You can imagine the horrors that occur in the application memory when it tries to call that method on a random integer, thinking that it's a pointer to an object.

Object is deallocated - why? where?

Ok, I spent the last 8 hours fighting with it - it just seems beyond me. Here's my complete (relevant) code:
- (void)updateUserDefaults
{
NSMutableDictionary *viewControllerDetails = [[NSMutableDictionary alloc] initWithCapacity:4];
[viewControllerDetails setObject:[NSNumber numberWithInt:OOVenueClassControllerType] forKey:#"classType"];
[viewControllerDetails setObject:self.searchTerm forKey:#"searchTerm"];
[viewControllerDetails setObject:self.searchLocation forKey:#"searchLocation"];
//----- the next two lines cause the problem
NSString *res = [[NSString stringWithFormat:#"%#",[searchResults xmlString]] retain];
[viewControllerDetails setObject:res forKey:#"searchresults"];
//-----
NSMutableArray *viewControllersList = [NSMutableArray array] ;
[viewControllersList addObject:viewControllerDetails];
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
//the following line causes the error
[defs setObject:viewControllersList forKey:kViewControllersKey];
[defs synchronize];
[res release];
}
Note the block with the next two lines cause the problem. At first I didn't create another string, but added it later while trying to solve the problem.
If I comment out those two lines, everything works fine. If I put them back in, I get
- [CFString class]: message sent to deallocated instance 0xa1a9000
Is something is wrong with the string that I'm trying to put into the userdefaults? That string is rather large (about 200,000 characters), but I had stored even longer strings in user defaults in the past.
It's also worth noting that if I uninstall the app, then everything works fine. But on subsequent runs the problem exhibits itself.
So, how and why and where is the string getting deallocated? I have explicitly added retain - but that still doesn't help. What am I missing?
Edit: just realised I forgot to say that the error is thrown on line
[defs setObject:viewControllersList forKey:kViewControllersKey];
Also, for general information, method - (NSString *)xmlString on searchResults does exactly what the name means: creates an XML string with the information from that object.
Edit 2: I tried doing something else with that string - convert it to NSData, compress using zlib - but regardless of data type, that particular object gets deallocated. Does it have to do something with the size of the string?
NSString *res = [[NSString stringWithFormat:#"%#",[searchResults xmlString]] retain];
Is auto released. You don't need to release it at the end of your method. You are over-releasing it.
Further, you don't need to retain the [searchResults xmlString]. The stringWithFormat method already does it for you.
Good Luck!
Ok, not sure what exactly the problem was, but it was somewhere in the searchResults and/or xmlString method. searchResults object is originally created from XML received from the server (XML is parsed into the object structure). When xmlString was called, for some reason the string I was getting back was different from the original XML (I'm not talking about formatting, of course) - of 200,000 char-long string, within the first 500 chars or so there were some differences. I haven't been able to figure out why. So, instead of recreating the xml from object structure, I instead stored the original XML in a field in that object and, when xmlString was called, simply returned the original string. Now everything worked fine.
Thank you all for your support through this painful process.

Trashed properties in Application Delegate

I'm in deep trouble. Something in my app causes a lot of properties in my app delegate to become trashed (changing contents and even object type, say an NSArray becomes an NSString...), and I can't debug it out. I can find no memory leaks or errors on my part. The only thing I've found is that all the way to ViewDidAppear for the view of the first tab, everything is okay. The view displays a table. When one of the cells are clicked, the app delegate properties are already trashed.
What after a view has been loaded and before didSelectCellForRow could cause this? No other code of mine is being executed between those two, certainly no code in the app delegate.
Any tips for hunting this down in an sleuthy manner would be appreciated, or just some thoughts on narrowing it down to what could cause it.
It sounds like either something is getting released prematurely or things are not properly connected with respect to one of your XIBs. If you haven't already, you might want to familiarize yourself with NSZombieEnabled, NSDeallocateZombies, NSEnableAutoreleasePool and NSAutoreleaseFreedObjectCheckEnabled. These are environment variables that can be set in the Executable "Get Info" window's Arguments panel.
For sanity's sake, I have added this to my AppDelegate's -applicationDidFinishLaunching:
#ifdef DEBUG
// account for environment value's actual value if set.
NSString *NSZombieEnabled = (getenv("NSZombieEnabled"))
? [NSString stringWithCString:getenv("NSZombieEnabled") encoding:NSASCIIStringEncoding]
: #"NO";
DLog(#"NSZombieEnabled = %#", NSZombieEnabled );
NSString *NSDeallocateZombies = (getenv("NSDeallocateZombies"))
? [NSString stringWithCString:getenv("NSDeallocateZombies") encoding:NSASCIIStringEncoding]
: #"NO";
DLog(#"NSDeallocateZombies = %#", NSDeallocateZombies );
NSString *NSEnableAutoreleasePool = (getenv("NSEnableAutoreleasePool"))
? [NSString stringWithCString:getenv("NSEnableAutoreleasePool") encoding:NSASCIIStringEncoding]
: #"YES";
DLog(#"NSEnableAutoreleasePool = %#", NSEnableAutoreleasePool );
NSString *NSAutoreleaseFreedObjectCheckEnabled = (getenv("NSAutoreleaseFreedObjectCheckEnabled"))
? [NSString stringWithCString:getenv("NSAutoreleaseFreedObjectCheckEnabled") encoding:NSASCIIStringEncoding]
: #"NO";
DLog(#"NSAutoreleaseFreedObjectCheckEnabled = %#", NSAutoreleaseFreedObjectCheckEnabled );
#endif
It sometimes saves me from having to check these variables through Xcode UI.
The only way out was to wade through every alloc in the app delegate and a few viewcontrollers and also made sure I knew what'd happen using the NSCopying protocol. There were 2 errors due to synthesized but nil (and then reassigned!) objects, and 1 copy-error, one or more of them caused the trashing when there was an early "autorelease" by Objective-C.

Objective C - Problem with objectForKey

Okay, I'm trying to write a high score function for my app.
My problem is that when no high score has been saved yet, my program crashes.
If I save it with:
[[NSUserDefaults standardUserDefaults] setObject:#"[given string]" forKey:#"firstName"];
first, it works fine. However, if I start up the program for the first time and try to view the high scores with the following code:
first = [[NSString alloc] initWithString:[[NSUserDefaults standardUserDefaults] objectForKey:#"firstName"]];
bad things happen.
Basically, is there away to see if nothing yet exist under firstName? Is there a way to initialize without erasing any name that might already be present?
Thanks.
The NSString documentation for initWithString: says
Parameters
aString
The string from which to copy characters. This value must not be nil.
The documentation for objectForKey: says
Return Value
The object associated with the
specified key, or nil if the key was
not found.
The problem seems to be that there is a nil returned when you try to retrieve firstName that doesn't exist yet and try to create a NSString with it as input.
The NSUserDefaults instance method registerDefaults: is meant for exactly this purpose: You can set default values for your preferences that will be overridden by any other value set for the same preference key. Just make sure to call it early enough that it will run before any code that needs to access your preferences.
You could load "first" like this:
first = [[[NSUserDefaults standardUserDefaults] objectForKey:#"firstName"] retain];
if (!first) {
// set default or do something else if there wasn't a value saved
first = #"N/A";
}