Cocoa - `stringWithContentsOfURL/dataWithContentsOfURL` causes ERROR? - iphone

The following code for some reason poradically works. I have checked the URL so many times it's not funny (It returns plain text that I would like to parse). The code was 100% functional then it just stopped working and started giving me a EXC_BAD_ACCESS error.
There is nothing in the debugging output to post other than a line saying the output is switching to the process twice. (Except sometimes something about a double release.)
So far (as much as I can remember) I have tried:
Reinstalling the app - it only has problems on the 'Default' run (not the first Run/initiate Run.)
Running the URL in the browser (chrome, firefox, IE...)
Putting the call in a #try / #catch block
Using retain
Using a temp NSAutoreleasePool
Splitting up / separating the elements of the call (along with loggin Everything - once it hits the error, nothing gets logged)
Using the dataWithContentsOfURL functions with the above
NSAutoreleasePool *tmpPool = [[NSAutoreleasePool alloc] init];
NSString *url_string = [self getNormalVersionDownloadURL];
NSLog(#"urlString: -%#-", url_string);
NSError *er;
NSURL *the_URL = [[NSURL URLWithString:url_string] retain];
NSString *version_String = [NSString stringWithContentsOfURL:the_URL encoding:NSASCIIStringEncoding error:&er];
NSLog(#"verions_string: -%#-", version_String);
if ([version_String length] < 16)
return;
[tmpPool release];
(NSAutoreleasePool and autorelease added due to http://discussions.apple.com/thread.jspa?threadID=1667544)
(Cashed page - http://webcache.googleusercontent.com/search?q=cache:8D7zlQdG9PMJ:discussions.apple.com/thread.jspa%3FthreadID%3D1667544+http://discussions.apple.com/thread.jspa%3FthreadID%3D1667544&cd=1&hl=en&ct=clnk&gl=us&source=www.google.com)

discussions.apple.com is currently down so I cannot read the discussion thread. At any rate:
NSString *url_string = [[self getNormalVersionDownloadURL] autorelease];
Does -getNormalVersionDownloadURL return an owned or a non-owned object? You only send -autorelease if the method returns an owned object.
NSError **er;
This should be NSError *er instead, or it should be initialised with the address of a variable of type NSError *. Since the latter is uncommon and unnecessary, the following assumes NSError *er.
NSURL *the_URL = [[NSURL URLWithString:url_string] autorelease];
+URLWithString: returns an NSURL object that you don’t own, hence you don’t (auto)release it.
version_String = [[NSString stringWithContentsOfURL:the_URL
encoding:NSASCIIStringEncoding error:er] autorelease]; //ERROR occurs here
Two problems:: +stringWithContentsOfURL: returns an NSString object that you don’t own, hence you don’t (auto)release it. Furthermore, the third parameter should be &er instead of er.

URLWithString and stringWithContentsOfURL are convenience methods and then already put the variable in autorelease I don't think you need to add autorelease while creating the_URL and version_String
try to remove autorelease ...

Related

iOS: understanding Problem with release an NSString

I have following code:
+ (NSDictionary*) JSONRequest: (NSString*)query andWithCredentials:(BOOL)withCredentials
{
if (withCredentials)
{
NSString *username = [LoginHandler GetUsernameFromNSDefaults];
NSString *password = [LoginHandler GetPasswordFromNSDefaults];
NSString *additionalQuery = [NSString stringWithFormat:#"login_username=%#&login_password=%#", username, password];
query = [NSString stringWithFormat:#"%#&%#", query, additionalQuery];
[username release];
[password release];
[additionalQuery release];
}
NSURLRequest *request = [NSURLRequest requestWithURL:
[NSURL URLWithString:query]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *jsonString = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSDictionary *results = [jsonString JSONValue];
return results;
[request release];
[response release];
[jsonString release];
[results release];
}
The problem is the Release of the additionalQuery-NSString.
When I run this code, it ends with an BAD-ACCES-EXCEPTION for the
[additionalQuery release];
As soon as I comment this out, the code works fine.
Now, as simple as it is, run my app without this line of code could be fine, but my question is: What do Im wrong?
I generate an NSString in an IF-Clause, then I CAN only release it in the IF-Clause. But why I got a Error there?
Look at your creation of additionalQuery
NSString *additionalQuery = [NSString stringWithFormat:#"login_username=%#&login_password=%#", username, password];
With stringWithFormat you create an autoreleased NSString object. You MUST NOT release it manually according to the Memory Management rules since you don't own it.
You own only things you created with alloc] init..] or something with new.. or create.. in the name and of course if you do a copy such as mutableCopy.
So [additionalQuery release] causes over-releasing an object and thus it is a BAD ACCESS
The problem is that the string instance is created using a class method that starts with the class name (stringWithFormat). By convention, these types of class methods return an autoreleased object, freeing you from worrying about releasing them unless you specifically call retain on the returned object.
If you do want to perform your own memory management on the object, you could change your line:
NSString *additionalQuery = [NSString stringWithFormat:
#"login_username=%#&login_password=%#", username, password];
to either of the following:
NSString *additionalQuery = [[NSString alloc] initWithFormat:
#"login_username=%#&login_password=%#", username, password];
or:
NSString *additionalQuery = [[NSString stringWithFormat:
#"login_username=%#&login_password=%#", username, password] retain];
As an aside, you also have several other issues with this code.
The username variable should not be released because again, by convention, the method you get it from GetUsernameFromNSDefaults should return an autoreleased object. As a general rule of thumb, any method other than an init method should return an autoreleased object. It would become very difficult for a programmer not knowledgable with the codebase to pick it up and modify without following these conventions.
The request variable does not need to be released because it is created with a class method that returns an autoreleased object (requestWithURL). If you wanted it to be retained by your code, either call retain on it, or use the method initWithURL:.
Additionally, the results variable is not retained by you, so there is no need to release it.
You don't have to release it manually, it will get autoreleased. ([NSString stringWithFormat:] vs. [NSString initWithString:])
additionalQuery was never retained, that I can see. (stringWithFormat does an autorelease, so it doesn't count.)
When you create an instance of an object using a convenience method (one that does not begin with new, alloc or copy) the returned object is autoreleased. In other words you do not need to explicitly release it.
When you invoke the stringWithFormat method it returns an autoreleased NSString. You subsequently go on to release this NSString instance...
[additionalQuery release];
This sends the release message to the additionalQuery instance. As it's an autoreleased object it is added to an autorelease pool which lives (usually) on the main event thread. This pool is drained frequently and subsequently sends a release message to each of the objects it contains. Hence when an object is autoreleased the pool will look after sending the release message for you.
Your EXC_BAD_ACCESS here is a result of you releasing the NSString - dropping its retain count to 0 prior to the pool draining. The pool is then drained and attempts to send a message to a deallocated object.
you have specified here [NSString stringWithFormat:#"login_username=%#&login_password=%#", username, password];
means this method will handle allocation and release for your string so "you do not need to release it" hence remove the line [additionalQuery release];
also, u are not allocating string for username and password hence no need to release it .
if you write Nsstring *username = [[NSString alloc]init]; then you need to release it..
for more information regarding Memory Management refer
http://marcelsite.heroku.com/posts/5-iPhone-s-alloc-init-new-retain-release-autorelease-copy-
this will really help you...

NSString stringWithFormat causing iPhone app to crash

When I construct a dynamic URL using NSString stringWithFormat and then use that value in my XML parser I get random crashes. However if I test it with a constant string it works fine...
This is my code for generating the string,
loginURL = [NSString stringWithFormat:#"%#%#",ScriptURLString,#"authenticate"];
Which results in,
http://edms.digistorm.com.au/test/index.php?s=&sc=D41D8CD98F00B204E9800998ECF8427E&m=authenticate
Then I use it in my XML parser,
XMLReturnData = [[NSMutableArray alloc] init];
xml = [[XMLParser alloc]
initWithXMLPath:loginURL
lookForElement:#"Authenticate"
setCallbackObject:self
withSelector:#selector(dataReady)
data:XMLReturnData
];
For some reason this is causing my app to crash. If I use a constant string like,
loginURL = #"http://edms.digistorm.com.au/test/index.php?s=&sc=D41D8CD98F00B204E9800998ECF8427E&m=authenticate";
it works fine...
loginURL is defined as NSString *loginURL; inside my header file for this view.
Any help or guidance would be much appreciated!
Thanks,
Tim
The method you are using to allocate the string is important.
You have two basic ways to allocate your string:
NSString *loginURL = [[NSString alloc] initWithFormat:#"%#authenticate", ScriptURLString];
Compared to:
NSString *loginURL = [NSString stringWithFormat:#"%#authenticate", ScriptURLString];
For the first, Cocoa conventions say that because you caused the object to be created via an alloc message you "own" it and are responsible for releasing it.
For the latter, the convention is that because you caused the object to be created by a class "convenience" method, you do NOT own it and are not responsible for releasing it. The class (here, NSString) has that responsibility which it will discharge through an autorelease pool.
To summarise, when you explicitly create something with an alloc/init, you must release it. When you use a [NSThing thingWithXXX] style method you must not.
This shows your string is get released and when you calls it in XML parser it crashes the app.
actually stringWithFormat gives a autorelease object for string.
So what you need,make your string as retain property inside .h then synthesize it in .m and release it in dealloc method.
and also do this,
in viewDidLoad
NSString *tempString=[[NSString alloc] init]; //using this because your string is retain type so it prevent increment in retain count.
self.loginURL=tempString;
[tempString release];
Now when you use stringWithFormat use like this
self.loginURL = [[NSString stringWithFormat:#"%#%#",ScriptURLString,#"authenticate"] retain];
It solves your problem.
Not to call release on loginURL, because you have'nt alloced it and only iOS have right to destroy it...
Use below code
loginURL = [[NSString alloc] initWithFormat:#"%#%#",ScriptURLString,#"authenticate"];
Once you used loginURL Do'nt forget to call release on it ...
[loginURL release];
Try this, it might help you.
loginURL = [NSString stringWithFormat:#"%#authenticate",ScriptURLString];

NSXMLParser throwing EXC_BAD_ACCESS

It is normal to have such issues but I am currently stuck in knowing how it works.
Whenever I use NSXMLparser to parse the URL and store in the database, it gets parsed for the first time but when I parse it again it throws EXC_BAD_ACCESS.
Here is my code:
- (void) initParse {
[super init];
appleAppDelegate = (appleAppDelegate*)[[UIApplication sharedApplication] delegate];
NSURL *url = [[[NSURL alloc] initWithString:#"http:example.com/file.xml"] autorelease];
self.parser1 = [[[NSXMLParser alloc] initWithContentsOfURL:url] autorelease] ;
[parser1 setShouldResolveExternalEntities:NO];
[parser1 setDelegate:self];
[parser1 parse];
}
When it reaches the end of the function at "}", it throws EXC_BAD_ACCESS. I am not sure what is wrong since I am releasing my URL and even my parser.
Has any one come across this situation.
Sagos
Try running with NSZombieEnabled - that will tell you the type of the object which is being accessed after being freed.
You are accessing a released object which is exactly your problem, make sure you release at the end and make sure everything you need is still around.

Can I store AVAudioPlayer references in an NSMutableArray and play them at will?

I have a library of 16 short sound clips I need to be able to play in quick succession. I realized that creating and preparing AVAudioPlayer objects in real time was too much to ask of the iPhone.
So instead, during my app's initialization, I am pre-creating a series of AVAudioPlayers so that each one is basically pre-loaded with one of my 16 sounds, so they're ready to be played at any time.
The problem is, to keep this clean, I would like to store the references for these 16 AVAudioPlayers in an NSMutableArray, so I can easily get at them just by knowing their array location. However, the way I'm doing it is crashing the simulator w/no error messages in the log:
Here is how I'm currently setting up the array of AVAudioPlayer references:
// (soundPlayers is an NSMutableArray instance var)
soundPlayers = [NSMutableArray arrayWithCapacity:(NSUInteger)16];
for ( int i = 0; i < 16; i++ ) {
NSString *soundName = [NSString stringWithFormat:#"sound-%d", i];
NSString *soundPath = [[NSBundle mainBundle] pathForResource:soundName ofType:#"mp3"];
NSURL *soundFile = [[NSURL alloc] initFileURLWithPath:soundPath];
AVAudioPlayer *p = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFile error:nil];
[soundFile release];
[p prepareToPlay];
[soundPlayers addObject:(id)p];
[p release];
}
Then later I try to load, say, sound #8 and play it back:
// (soundPlayer is an AVAudioPlayer instance var)
self.soundPlayer = [soundPlayers objectAtIndex:(NSUInteger)8];
[soundPlayer play];
Any ideas? Also does anyone know if any of the slick debugging tools that come with XCode would be useful for this type of problem?
I'm just guessing here, but there might be a problem with the capacity of the array.
Try: soundPlayers = [[NSMutableArray alloc] init]; instead.
This isn't likely to be a cause of the crash, but try NSLogging self.soundPlayer before calling -play on it to make sure that its not nil. And another minor little thing: you don't need to typecast p when adding it to the array. Just call [soundPlayers addObject:p] without (id).
I'm not sure of the exact cause of your crash, just going through the code and pointing out what doesn't look right. Hope this helps!
It is most likely the case that your soundPlayers mutable array is being released at some point since you aren't retaining it. I would ensure your property for the array is set to retain and do what Macatomy previously stated:
self.soundPlayers = [[NSMutableArray alloc] init];
Or even initWithCapacity:
It looks OK to me except perhaps you shouldn't call prepareToPlay before you put them into the array.
It's probably better to do this when you actually are going to play them. The description of that method says it preloads buffers and acquires the audio hardware for playback. Doing it 16 times at once may be too much.
EDIT - you should also add error checking, your missing results that might tell you something is wrong, initWithContentsOfURL:outError: returns an error message. Take advantage of that:
NSError* error = nil;
AVAudioPlayer *p = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFile error:&error];
if (error) {
NSLog(#"ERROR: %#, URL=%#", error.localizedDescription, soundFile);
}

NSThread crashes on second call (iPhone)

I have an object and in that object I start my thread (for loading doing some URL loading).
When I have a return of my data I call a selector to perform on the main thread.
Works fine if I call it the first time, but the second time it crashes (no specific error).
[NSThread detachNewThreadSelector:#selector(doThread:)
toTarget:self
withObject:#"lala"];
-(void) doThread:(NSString *)poststring {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
DataModelLocator *mydelegate = [DataModelLocator instance];
NSData *postData = [poststring dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:NO];
NSURL *url = [NSURL URLWithString:[mydelegate fwaservicepath]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
[request setHTTPBody:postData];
NSURLResponse *urlResponse;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:nil];
if(data) {
[self performSelectorOnMainThread:#selector(loadDidFinishWithData:)
withObject:data
waitUntilDone:YES];
//[self loadDidFinishWithData:data];
} else {
[self performSelectorOnMainThread:#selector(loadDidFinishWithError:)
withObject:data
waitUntilDone:YES];
}
[pool release];
}
}
It crashes when I call performSelectorOnMaintThread... Could it be that it crashes on a singleton, when it got released?
When dealing with threads, avoid autoreleased objects like the plague. The autorelease pools will be drained at nondeterministic times, causing fun crashes. Use alloc/init and release on all objects involved, making sure to retain all objects that you take in on methods that are called from another thread using performSelectorOnMainThread or detachNewThreadSelector.
Garbage collection on the Mac effectively solves these problems, but the iPhone is not going to have that any time soon.
You may want to post some more information about your problem (which of the two lines crashes, what you've figured out from debugging so far, etc.) so that we can offer you some better suggestions. Without knowing which line is giving you problems, I'll hazard a guess: from the sound of it, you might have an object somewhere that's getting cleaned up by the automatic garbage collection.
Where is the variable "data" coming from? If you're creating it in the header file as a private member variable, you might have something like:
NSSomeType *data = [NSSomeType builtInInitFunction];
A variable initialized like this will normally be autoreleased, but you probably want to make sure that the garbage collection retains the instance of that object. Try something like:
// Objects initialized with init are retained
NSSomeType *data = [[NSSomeType alloc] init];
// Objects that would normally be autoreleased can be marked as retain
NSSomeType *data = [[NSSomeType builtInInitFunction] retain];
I'm not sure how your code is structured, but be sure to add at least one release for every retain and init! I'm still pretty new to Objective-C, so it's a little bit like the blind leading the blind so take my advice with a grain of salt.
Check out the "More on Memory Management" section of Learn Objective-C for more info.
EDIT2: Clarified example code. Thanks to Evan (comments) for the help.
EDIT3: I agree with Brad. Consider removing the AutoRelease pool you have an handling your alloc/init/release yourself. I don't know enough about the NSURLConnection object to know this, but is your *data memory being marked as Autorelease? If so, you may need to initialize in a different way or use retain.
Step through your code in the debugger. Figure out A) exactly which line is crashing and then B) the values of all of your variables. If you're lucky, you'll notice one is nil.