MemoryLeak problem in following case

2019-08-04 10:45发布

问题:

I am getting a memory leak when i click the play button....

I am testing with that "Leak" tool under "Run and performance tool"....on simulator

I am getting that leak when i click the play button first time.....

Here is my code....

-(IBAction)play
{


    [self setPlayer];
    [self playme];  
}



-(IBAction)stop
{
    [self stopme];
    [self releasePlayer];

}


-(void)setPlayer
{

    NSURL *file = [[NSURL alloc] initFileURLWithPath:
                   [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"shut up.mp3"]];
    NSError *err = nil;
    player = [[AVAudioPlayer alloc] initWithContentsOfURL:file error:&err];

    [file release];
    player.numberOfLoops = -1;
    [player prepareToPlay];
    player.volume=1.0;


}


-(void)playme
{
    if (!isPlaying)
    {
        [player play];
        isPlaying=YES;
    }
}


-(void)stopme
{
    if (isPlaying)
    {
        [player stop];
        isPlaying=NO;
    }
}

-(void)releasePlayer
{
    if(!isPlaying)
    {
        [player release];
        player=nil;
    }
    isPlaying=NO;
}

回答1:

I think, the below statement is the source of memory leak,

player = [[AVAudioPlayer alloc] initWithContentsOfURL:file error:&err];

Here is the SO posts which has discussed the same issue.

AVAudioPlayer memory leak

AVAudioPlayer memory leak

AVAudioPlayer Memory Leak - Media Player Framework

Here is the blog post

AVAudioPlayer Memory Leak

EDITED:

As per the blog tutorial your code must be look like below.

-(void)setPlayer
{

    NSURL *file = [[NSURL alloc] initFileURLWithPath:
                   [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"shut up.mp3"]];
    NSError *err = nil;

    NSData *data = [NSData dataWithContentsOfFile:file];
    AVAudioPlayer *player = [AVAudioPlayer alloc];    
    if([player initWithData:audioData error:NULL])
     {

        player.numberOfLoops = -1;
        [player prepareToPlay];
        player.volume=1.0;
        [player autorelease];
    } 
    else 
    {
        [player release];
        player = nil;
    }
    [file release];
}

The leak-free version stores the pointer returned by alloc, rather than the pointer returned by initWithData:error:. That way, whatever happens, the player can still be released.



回答2:

The blog post in Jhaliya's answer describes a leak that's specific to the situation when your player can't init the audio, for example when it can't find the file.

The real problem with your code is that you only release the player if the user explicitly stops the audio. If the audio plays through to the end, you have a player instance with a retainCount of 1. Then if the user hits play again, you create a new player and assign it to the player variable, leaking the old one.

The easiest solution to this is to make player a retained property:

@property(nonatomic,retain)AVAudioPlayer *player;

Then, instead of assigning to the ivar directly, use the mutator to set the player, which will implicitly release the previously set instance, if there is one:

[self setPlayer:[[[AVAudioPlayer alloc] initWithContentsOfURL:file error:&err] autorelease];

And don't forget to release it in your dealloc:

-(void)dealloc {
    [player release];
    [super dealloc];
}