I am developing a quiz app. I take questions from a xml file, parse it and display random questions. These are stored in NSdictionary and NSMutableArray. Also the app plays background music and sound for clicking of buttons(AVAudioPlayer). and vibration( AudioServicesPlaySystemSound(kSystemSoundID_Vibrate))
In one particular function if i try to release the temp variables that I use(I.E NSDictionary and NSMutableArray) the app crashes wen i reach that function for the second time. Hence if i don release these, it works fine but eventually crashes with a "EXC_BAD_ACCESS" ERROR. It does not point out to any line or function.
When i used the tool "LEAKS", it showed i was having around 7000 leaks. I don understand how to use that tool but I am sure that i am not creating so many variables, jus a few and even those I release.
And just once i got the ERROR " data formatters temporarily unavailable".
Any Idea what i am doing wrong?? F1 :)
PS: my code is all simple stuff, plus i donno what the problem is hence i donno what code to post here?
Also i would like to know if i create a NSString* in a function for temp use should i release it at the end of the function?(i do release it)
EDIT:
-(void) loadQuestion
{
strCorrectAnswer = #"";
int intQuestionNo;
NSString *strQuestionNo = [[NSString alloc] init];
// get random question out the xml file
NSDictionary *dctQue = [dctQuestions objectForKey:strQuestionNo];
// blah blah
// jumble the answers to be displaed
NSMutableArray *answerJumble = [[NSMutableArray alloc] init];
NSMutableArray *answers =[NSMutableArray arrayWithObjects:[dctQue objectForKey:#"WrongAnswer1"],[dctQue objectForKey:#"WrongAnswer2"],[dctQue objectForKey:#"WrongAnswer3"],[dctQue objectForKey:#"CorrectAnswer"],nil];
// blah blah
/*
[strQuestionNo release];
[answers release];
[answerJumble release]; */
}
You should read something about memory management in Cocoa. See the Mac Developer Center or the tutorial at Cocoa Dev Central. Memory management on iPhone is not hard, it’s a pity to code by trial and error.
well after a bit of digging, the problem was wen the sound file had to be replayed. If i press a button and before the sound file finishes playing if i press again, the sound file was being played just once. Resulting in a memory leak of 3000.
IF i did this twice the app wud crash after a leak of 6425. Hence the ERROR-"data formatters currently not available".(i guess)
Related
I currently need to upload large files from an iDevice to a server API. For this I'm attempting to use the ASIHTTPRequest library as it automatically supports uploading large files in queued chunks (before this I simply created an NSData instance with the bytes for the entire file at once and attached this to the POST message, but this causes the application to crash on larger files due to an excessive amount of RAM usage).
The problem is that when you need to upload files and add a file to the HTTP Post message, this is the syntax you need to use:
[theUploadRequest setFile:#"" forKey:#"videoupload"];
setFile requires a file path in a string format. The problem I'm currently having is that it does not seem like you are allowed to simply take the file path from a file which is not in your applications sandbox? Since I need to upload a file which is not in my application, but outside of it in the standard cameraroll.
I tried to make this quick test to see if I could create an NSData object and fill it with data from a file in the cameraroll, providing a path to it like this:
NSData *testData = [NSData dataWithContentsOfURL:theContent.defaultRepresentation.url];
NSLog(#"THE SIZE OF THE TEST DATA: %i", testData.length);
Note that "theContent" is an instance of an ALAsset, and is a file retrieved from the cameraroll. The result of this is simply a length of 0, which I suppose means you can't simply do that.
Is there any way around this? Or would I have to somehow import the video file into the application's sandbox?
So if I understand correctly, you want to upload stuff straight from the camera roll? Based on the docs I'd say the important piece of code you need is:
NSString *filePath = [[info objectForKey:
UIImagePickerControllerMediaURL] path];
The info dictionary is passed to your media picker's delegate:
- (void) imagePickerController: (UIImagePickerController *) picker
didFinishPickingMediaWithInfo: (NSDictionary *) info;
You should then be able to use that filePath in your call to setFile.
Take a look here probably you'll find something useful.
Hope this helps.
EDIT:
For something simpler you can use the bit of code posted in this answer
Implying that you need to copy the file somewhere before uploading it, especially if it is a big one.
I had uploaded my app to the App Store but it was rejected two times, with the reason being that the app is crashing on iPhone 4 (iOS 4.3.5). I have tested my app on the same version but I can't make it crash.
My app is getting user location for drawing a path on g-map. I have also tested with location service enable/disable, wifi on/of. For all these ways I have tested, my app is not crashing.
After symbolicating my last crash report I have found location and that is in cellForRowAtindexpath:
if([finalarray count]>0)
{
cell.shopnamelbl.text=[[finalarray objectAtIndex:indexPath.row] valueForKey:#"name"];
cell.distancelbl.text=[NSString stringWithFormat:#"%#km",[[finalarray objectAtIndex:indexPath.row] valueForKey:#"distance"]];
}
else if([unsortedarray count]>0)
{
cell.shopnamelbl.text=[[unsortedarray objectAtIndex:indexPath.row] valueForKey:#"name"];
cell.distancelbl.text=#"";
}
else
{
cell.shopnamelbl.text=#"Loading...";
cell.distancelbl.text=#"";
}
return cell;
in above code on cell.shopnamelbl.text=#"Loading..."; this line is the cause of my crash, but in my testing everything works perfectly. I am not seeing a crash anywhere else in my app.
So now what can I do to fix this?
There's nothing obviously wrong with the code you've posted, so it's likely to be some other difference between what you've been testing and what Apple have on their handsets.
On first run, their device will have nothing in NSUserDefaults set, nothing in the keychain, nothing in the Documents directory. You need to completely wipe your device of any trace of previous runs of your app. Deleting and re-installing isn't enough.
It might also be worth trying on a number of devices. It's possible that you've inadvertently made some assumptions about how long certain operations take which don't hold on the faster/slower/different handsets that Apple uses for testing.
I understand that it may be possible to speed up flite TTS in iOS by removing unused voices. How is this done?
I have some text that seems to take about 45 seconds to convert (on iPhone 3G) but I only have 30 seconds. I am starting the conversion as soon as possible and preloading the AV player already but I am still a little too slow.
This trick will not really speed up flite but will free up the device to do other things while flite is doing it's thing:
Add this method:
- (void)speak:(NSString *)message {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[fliteEngine speakText:message];
[pool release];
}
When you want ti to speak, call it with this:
[self performSelectorInBackground:#selector(speak:) withObject:#"Text to speak!"];
This will put your app into multi-treaded mode and will render the speech and play it as a background thread. This will allow your app to go off and do other things while this is happening. I have also noticed that it seems to render the text a little faster this way. Hopefully it will give you the little bit you need.
Removing voices makes no difference in the speed. One voice, "cmu_us_kal" is much faster than the others. I did not perform speed tests on all of the others but kal is at least 5 times faster than "cmu_us_rms" which I had been using.
You can convert in shorter sentences or phrases. Then start playing one converted speech segment while still converting the rest in a background thread.
in my iPhone app, I am using the default NSFileManager to copy and move some data around. The amount of data may be some MB, so it may take several seconds to finish copying. If the user now starts a copy process and quits the application, can I continue writing to disk in background mode? If so, how? I do not want to start a new copy process, but only finish the running one.
I am currently using two kinds of method calls:
[imageData writeToFile:path atomically:YES];
and
[fm copyItemAtPath:sourcePath toPath:destinationPath error:&error];
Both calls are wrapped in another method and are performed in the background via -performSelectorInBackground:withObject:.
Another question is if I can get any information on how far the writing operation has progressed or at least if it has already finished?
EDIT: Some more information:
Currently, I did not implement any background tasks at all. What happens to running disk operations when the user presses the home button? Are they just cut off and the file is incomplete? Can I continue writing on the next start? Are they canceled and the incomplete file is removed?
To sum it all up: I am writing data to disk and I want it to stay consistent when the user presses the home button during a writing operation. How do I achieve this?
By default you app does not really finishes when the user press the home button. Hence it should finish the that task as long is does not take too long. If it takes long then please take a look at this question: How to implement Task completion
One thing: I think you are confused about what performSelectorInBackground:withObject: really does.
The background used in "... I continue writing to disk in background mode" and the background in "performSelectorIn Background :withObject: " are not the same background
The former background:
Is when you app becomes invisible to the user, but is still running, at least for a while. (When the user presses twice the home button and change to another app)
The latter background:
Refers to a background thread, which is the opposite to the main thread.
In this case, if you use or not performSelectorInBackground:withObject: it will have no effect in whether you app can do it background mode or not. These are completely different things
You can set BOOL finished = YES right after [fm copyItemAtPath:ToPath:error:]; and save it in NSUserDefaults and check that flag when your app comes to the foreground again ;)
Hope it helps ;)
You may want to look into NSOperationQueue, the documentation page is here.
Also there are two existing Stack Overflow questions that address using NSOperationQueue that are here and here. Both have accepted answers.
As for figuring out whether a file write has completed, I had this issue as well when I was writing an OS X application that created a very large file. I wanted the user to be able to track the progress of the write. I ended up using an NSTimer along with a UIProgressBar
Essentially, you will want to determine the (expected) total size of the file. Then, as you write the file, you should have another method (in the code below I have checkFileWritingProgress) that you can call at periodic intervals using an NSTimer. Check the current progress against the total expected file size and update the UIProgressBar accordingly.
I have provided some code to get you started.
- (void)checkFileWritingProgress:(NSTimer *)someTimer {
fileAttributes = [fileManager attributesOfItemAtPath:[NSString stringWithFormat:#"%#.data",saveLocation] error:nil];
currentFileSize = [fileAttributes fileSize]; // instance variable
if(currentFileSize < maxFileSize) { // maxFileSize is instance variable
[progressBar setDoubleValue:(((double)currentFileSize/(double)maxFileSize)*100)];
// progressWindows OS X only... not on iOS
//[progressWindow setTitle:[NSString stringWithFormat:#"Writing... | %.0f%%",(((double)currentFileSize/(double)maxFileSize)*100)]];
}
else {
[progressBar setDoubleValue:100.0];
}
}
And the timer...
NSTimer *timer = [[NSTimer scheduledTimerWithTimeInterval:0.01
target:self
selector:#selector(checkFileWritingProgress:)
userInfo:nil
repeats:YES] retain];
//(linked to timer... this is the
// CORRECT way to use a determinate progress bar)
[progressBar setIndeterminate:NO];
[progressBar setDoubleValue:0.0];
[progressBar displayIfNeeded];
I hope this code helps. Let me know if anything needs to be clarified.
I'm trying to play a sound file from an iPhone program.
Here's the code:
NSString *path = [[NSBundle mainBundle] pathForResource:#"play" ofType:#"caf"];
NSFileHandle *bodyf = [NSFileHandle fileHandleForReadingAtPath:path];
NSData *body = [bodyf availableData];
NSLog( #"length of play.caf %d",[body length] );
NSURL *url = [NSURL fileURLWithPath:path isDirectory:NO];
NSLog( [url description] );
NSLog( #"%d", AudioServicesCreateSystemSoundID((CFURLRef)url, &soundID) );
The first NSLog is to check that I have access to the file (I did), the second NSLog is to show the file URL, and the third NSLog returns -1500 "An unspecified error has occurred."
For the second NSLog, I get the following output:
file://localhost/Users/alan/Library/Application 敲慬楴敶瑓楲杮upport/iPhone蒠ꁻތĀ⾅獕牥⽳污湡䰯扩慲祲䄯灰楬慣楴湯匠灵潰瑲椯桐湯楓畭慬潴⽲獕牥䄯灰楬慣楴湯⽳䙂㕅㡂㤱䌭䐳ⴸ䐴䙃㠭㍃ⴷ䍁㈶㠵䙁㤴㈰䰯捯瑡䵥灡⽰汰祡挮晡imulator/User/Applications/BFE5B819-C3D8-4DCF-8C37-AC6258AF4902/LocateMe.app/play.caf
This is either due to my misunderstanding of the "description" method, or this contributes to the problem.
Any idea what is going wrong?
The first parameter to NSLog is a format string; you're passing [URL description] as the format string to the second use of NSLog. That's bad, because if the description of the URL contains any % characters then it will wind up printing random stuff from the stack.
Instead, write
NSLog(#"%#", URL);
You don't need to even use -description here; NSLog will invoke it for you automatically because %# means "an object," not "an NSString," and it's smart enough to do the right thing for you.
In addition, it seems that the iPhone simulator does not like to play (systemSound) sounds over 5 seconds in length. I had the same issue as out apps almost always play a start up systemSound.
One of the apps I was working on the simulator would not play the sound but the device would. Later I found that there is some strange limitation on the simulator and the way it handles sounds. I've filed a bug with Apple. Hopefully this will be address with the release of the iPhone SDK 3.0.
An update on this.
I got my keys to allow me to run my app on the device.
I found that my .caf file successfully played when the app ran on the device.
I then tried various alternatives.
A .wav file that is 60 seconds long failed to work on the simulator and the device.
A .wav file that is 5 seconds long would work on the simulator, but not on the device.
A .aiff file that is 5 seconds long works on the simulator and the device.
It would be good to know definitively what the simulator and the device are checking about the file when it is passed to AudioServicesCreateSystemSoundID
I tried the same with my application that plays sounds just fine. The sounds don’t play in the Simulator and when I try to NSLog the URL, I get the same garbage as You and EXC_BAD_ACCESS on the top of it. When I log the URL on the device, the URL is fine, but I get EXC_BAD_ACCESS nevertheless. When I drop the logging, sounds play and everything works. If somebody could explain this behaviour I’d be grateful. As for Your problem, I’d drop the logging and try the code on the device.
Use "AVAudioPlayer" class for playing .caf files