问题
I am runung Instruments on an iPhone 4S. I am using AVAudioPlayer inside this method:
-(void)playSound{
NSURL *url = [self.word soundURL];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
if (!error) {
[audioPlayer prepareToPlay];
[audioPlayer play];
}else{
NSLog(@"Problem With audioPlayer on general card. error : %@ | url %@",[error description],[url absoluteString]);
}
I am getting leaks when playing the sound files:
Leaked objects:
1.
Object: NSURL
Responsible Library: Foundation
Responsable Frame: Foundation -[NSURL(NSURL) allocWithZone:]
2.
Object: _NSCFString
Responsible Library: Foundation
Responsable Frame: Foundation -[NSURL(NSURL) initFileURLWithPath:]
Instruments does not point directly to my code so I find it hard to locate the leak reason.
MY QUESTION
What could cause the leak? OR How can I locate leaks when I am not responsible to the code?
EDIT This is the schema from Instruments cycles view:
Thanks Shani回答1:
Looks to be a leak in Apple's code... I tried using both
-[AVAudioPlayer initWithData:error:]
and-[AVAudioPlayer initWithContentsOfURL:error:]
In the first case, the allocated AVAudioPlayer
instance retains the passed in NSData
. In the second, the passed in NSURL
is retained:
I've attached some screen shots of the Instruments window showing the retain/release history for a passed in NSData
object.
You can see the AVAudioPlayer
object then creates a C++ object AVAudioPlayerCpp
, which retains the NSData again:
Later, when the AVAudioPlayer
object is released, the NSData
is released, but there's never a release call from the associated AVAudioPlayerCpp
... (You can tell from the attached image)
Seems you'll have to use a different solution to play media if you want to avoid leaking NSData/NSURL's..
Here's my test code:
-(void)timerFired:(NSTimer*)timer
{
NSString * path = [[ NSBundle mainBundle ] pathForResource:@"song" ofType:@"mp3" ] ;
NSError * error = nil ;
NSData * data = [ NSData dataWithContentsOfFile:path options:NSDataReadingMapped error:&error ] ;
if ( !data )
{
if ( error ) { @throw error ; }
}
AVAudioPlayer * audioPlayer = data ? [[AVAudioPlayer alloc] initWithData:data error:&error ] : nil ;
if ( !audioPlayer )
{
if ( error ) { @throw error ; }
}
if ( audioPlayer )
{
[audioPlayer play];
[ NSThread sleepForTimeInterval:0.75 ] ;
[ audioPlayer stop ] ;
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// ...
[ NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector( timerFired: ) userInfo:nil repeats:YES ] ;
// ...
return YES;
}
回答2:
As per the previous answers, all of my own research and the discussions in the apple developer forums points to the problem being present in Apple's own libraries in iOS versions 6.0, 6.0.1 and as far as I can tell 6.0.2 as well.
iOS 6.1 has fixed this issue and I can no longer see the leaks any more.
Sadly, this means that if you believe that your App will be run on phones still running versions 6.0, 6.0.1 or 6.0.2 of iOS, you will have to do workarounds to remedy the leaks for these cases.
What can be good to know is that the retainCount for whatever has been used to initialize the AVAudioPlayer seems to be 1 if everything else has been cleared as intended.
My own workaround is to encapsulate the audio playing in its own class, for which I do manual memory handling using the -fno-objc-arc
preprocessor flag when compiling.
When it comes to the time to release everything I make sure to fetch the retain count of the NSURL which I've used for initialization (will work the same if initialized with NSData) before actually commencing the release of it.
If everything else is handled correctly, by now it should be either 1 or 2, so simply release it the right amount of times.
Also, make sure to fetch the retain count before starting the release, otherwise you will be trying to access a freed object on iOS versions where the bug has been fixed.
回答3:
This is a problem with the Apple library itself. Many of the posts (on stackoverflow also) have reported the similar problem. There's nothing you can do here.
回答4:
It is my understanding that when working in ARC projects, you as the developer are no longer in charge of retains / releases, therefore the issue is with Apple's library and not your code.
回答5:
If this is truly a bug in Apple's Library, here is a not so fun workaround.
For the class in question, make it so that it does not have arc enabled.
This can be done by going to the target's build phases.
Go to the compile sources drop down and find the .m file for your class.
enter -fno-objc-arc
compiler flags column
Unfortunately, you will need to adjust the class and manually manage memory.
A more detailed description for configuring a class to not be arc enabled was on this answer on another question on SO
EDIT
I suppose you could encapsulate the AVAudioPlayer behavior to another class, and use that class for all your playback. In that case, you could set -fno-objc-arc
for the one file and not have to work on the memory management of your entire app
来源:https://stackoverflow.com/questions/12498015/leak-from-nsurl-and-avaudioplayer-using-arc