How to debug NSZombies & Memory Leaks? - ios

I am working on a basic game, that connects to a server and gets JSON data. It works fine for a few games, but crashes soon after due to memory pressure. I ran through instruments and came across something rather disturbing. Almost every instance variable being instantiated by [[Class alloc]init] was being leaked as a NSZombie object.
As you can see in the image, in 5 seconds I seem to have generated 9000 leaks.
I am using ARC.
Further analysis showed I was leaking when used certain methods:
-(void) playTimeUp
{
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"Gameover"
ofType:#"wav"]];
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
if (audioPlayer && soundShouldPlay){
[audioPlayer setDelegate:self];
[audioPlayer prepareToPlay];
[audioPlayer setVolume:.20];
[audioPlayer play];
[self.audioPlayers addObject:audioPlayer];
}
}
Also I use dataWithContentsOfUrl method quite often.
dispatch_async(kBackgroundQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:completeUrl];
[self performSelectorOnMainThread:#selector(startMethod:) withObject:data waitUntilDone:YES];
});
Could anyone tell me how to salvage this situation, or what I am doing wrong.

That's in the nature of zombie objects. Turning on zombie objects to debug the use of objects after they have been deallocated will obviously turn any such object into a leak. You can't debug using zombies and search for memory leaks at the same time.

I'm assuming that your memory leaks will be happened due to NSData Object being live on memory.
Did you try to save your NSData to documents folder as a file instead of NSData object?
Example
[data writeToFile:filePath atomically:YES];

Related

AVAudioPlayer Freezes the UI when new sound is played

I am new to iOS and I was searching fix for this issue since morning and didn't find the solution yet, I have more than 6000 sound files online which i want to play using AVAudioPlayer when user click on the cell, so i implemented it in didSelectRowAtIndexPath:
like this :
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#", [soundsArray objectAtIndex:(int)indexPath.row]];
NSData *soundData = [NSData dataWithContentsOfURL:url];
_audioPlayer = [[AVAudioPlayer alloc] initWithData:soundData error:NULL];
[_audioPlayer play];
}
the problem is, whenever i click on any row, the whole UI freezes till the time it takes to load the complete sound file from internet,
for larger files , it takes longer.
I tried with AVPlayer and it is working fine, but I need AVAudioPlayer becouse it seems easy to me to work with
-(void)AVAudioPlayerDidFinishPlaying:(AVAudioPlayer *)Player successfully:(BOOL)flag
thats why I want to implement it with AVAudioPlayer
I hope you understood my problem,
Sorry for my bad English.
Never use
NSData *soundData = [NSData dataWithContentsOfURL:url];
this will block your UI.
You need to use NSURLSession to download data asynchronously.

How to release the NSDATA from AVAudioPlayer?

Here I used to play the songs using AVAudioPlayer Framework. Hopefully it's playing, but if I click on other songs from the Songslist I will check the condition :
if(player.isPlaying == YES)
{
[player stop];
// And also i need to release or remove the existing NSDATA from the Player. otherwise the player won't release the existing data. So the memory pressure occurring in my project.
}
self.audioPlayer = [self.audioPlayer initWithData:m_currentMusic.fileData error:&error];
[self.audioPlayer prepareToPlay];
[self.audioPlayer play];
AVAudioPlayer#data is a read-only non-retained property. So, the data is not retained and you have no need to releease it (it's assigned as by default).
However, as I said, it's read-only so you can't set it if you wish to change tracks. You also shouldn't call init... on an already initialied object, as in your code:
self.audioPlayer=[self.audioPlayer initWithData:m_currentMusic.fileData error:&error];
I believe the intended action for what you want is to relesae your existing self.audioPlayer and re-create a new one, as follows:
// stop player as in your original code...
[self.audioPlayer release];
AVAudioPlayer *newPlayer = [[AVAudioPlayer alloc] initWithData:m_currentMusic.fileData error:&error];
self.audioPlayer = newPlayer;
[self.audioPlayer setDelegate:self];
[newPlayer release];
Note that the release will cause your original track data to be reclaimed (as it's non-retained).
See also Core Audio Overview

AVAudioPlayer playback results in class _NSThreadPerformInfo autoreleased with no pool in place - just leaking

Mucho tiempo wasted on this - I have fairly straightforward playback using AVAudioPLayer but when the file finishes playing I get this message in the debug window:
objc[39752]: Object 0x7304d80 of class _NSThreadPerformInfo
autoreleased with no pool in place - just leaking - break on
objc_autoreleaseNoPool() to debug
Here are my play and callback functions (I have simplified the filename creation but the error is the same):
#implementation PlaybackEngine {
int _pbIndex;
AVAudioPlayer *_player;
NSArray *fileList;
BufferStores *_BS;
}
....
-(BOOL)startPlayback {
NSURL *playURL = [[NSURL alloc] initWithString:[[DOCUMENTS_FOLDER stringByAppendingPathComponent:#"28Jan13_17:13:21.aif"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
printf("Playback file URL = %s\n", [[playURL path] UTF8String]);
_player = [[AVAudioPlayer alloc] initWithContentsOfURL:playURL error:nil];
_player.delegate = self;
[_player prepareToPlay];
[_player play];
self.playing = YES;
return YES;
}
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
// check for player stopping due to bad audio data
if (!flag) {
printf("Player finished playing due to bad audio data");
}
self.playing = NO;
_player = nil;
}
I've tried setting a breakpoint on objc_autoreleaseNoPool but it never gets hit. What blindingly obvious ARC mistake am I making?
Well I seem to have solved it. Apple Dev Support told me that this error should never happen, which made me think it must be some obscure bug in the 5.x SDK, but in the end I tidied up all my classes, redefining ivars as either properties or properties in an extension as appropriate, made sure all my property modifiers were correct etc and the problem has gone.
Sorry I can't explicitly pinpoint the culprit, but clearly a memory management issue.

Should I release NSBundle in sound?

Should I release NSBundle in the below code or not? NSURL should also be release or not?
I am confused.
NSBundle *mainBundle = [NSBundle mainBundle];
NSError *error;
NSURL *audioURL = [NSURL fileURLWithPath:[mainBundle pathForResource:#"count_in" ofType: #"mp3"]];
AVAudioPlayer *player1 = [(AVAudioPlayer*) [AVAudioPlayer alloc] initWithContentsOfURL:audioURL error:&error];
self.player = player1;
[self.player play];
[player1 release];
You should not release NSBundle and NSURL instances because you haven't alloced these.
From the apple Documentation.
You take ownership of an object if you create it using a method whose
name begins with “alloc”, “new”,
“copy”, or “mutableCopy” (for example,
alloc, newObject, or mutableCopy), or
if you send it a retain message.
You use release or autorelease to relinquish ownership of an object.
autorelease just means “send a release
message in the future” (specifically:
when the used autorelease pool
receives a drain message—to understand
when this will be, see “Autorelease
Pools”).
I would highly recommend you to clear your memory management concept.
Read the apple article on
Memory Management Rules

Memory Leak Copying data From server

for (int i=0; i<[images count] ;i++) {
url=#"http://192.168.0.101/titan/titanimages/";
url=[url stringByAppendingString:[images objectAtIndex:i]];
//NSData *imageData=[[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
NSData *imageData=[NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
destinationPath=[documentsDirectory stringByAppendingString:#"/modelimages"];
destinationPath=[destinationPath stringByAppendingPathComponent:[images objectAtIndex:i]];
[imageData writeToFile:destinationPath atomically:YES];
value=value+divideValue;
printf("%f\n",value);
[NSThread detachNewThreadSelector:#selector(updateProgressBar)toTarget:self withObject:nil];
}
This code has a memory leak: it does not release memory of NSdata and after some time memory utilization of application reaches 61 MB. Can anyone help me get out of this?
Not 100% sure, but it probably has to do with the use of the "convenience constructor" with the NSData class in particular. When you call "dataWithContentsOfURL" you'll get back an NSData object that is automatically auto-released. However, your current NSAutoreleasePool might not be in a scope that will result in that memory being released until the application exits. You could try switching back to the alloc/init call you have commented out, and try manually releasing each NSData object inside the loop, to guarantee that the NSData memory is released for each instance of NSData created in the loop (after you've saved off the NSData to file).

Resources