Trying to fix memory leak using AVAudioPlayer
In an attempt to fix a memory leak, I am trying to implement this code in my .m file:
AVAudioPlayer *audioPlayer;
- (AVAudioPlayer *)audioPlayerWithContentsOfFile:(NSString *) path {
NSData *audioData = [NSData dataWithContentsOfFile:path];
AVAudioPlayer *pla开发者_开发百科yer = [AVAudioPlayer alloc];
if([player initWithData:audioData error:NULL]) {
[player autorelease];
} else {
[player release];
player = nil;
}
return player;
}
Then attempting to call it via:
[audioPlayer audioPlayerWithContentsOfFile: [NSString stringWithFormat:@"%@/knock_knock.wav", [[NSBundle mainBundle] resourcePath]]];
Header file has:
- (AVAudioPlayer *) audioPlayerWithContentsOfFile: (NSString *) path;
Compiles with one warning: AVAudioPlayer may not response to '-audioPlayerWithContentsOfFile:' (Messages without a matching signature will be assumed to return 'id' and accept '...' as arguements.
Everytime I thing I am getting this language, I run into stuff like this that I spend hours trying to figure out. Anyone able to help? As always, thanks in advance.
Geo...
First of all, try to avoid statements like this:
AVAudioPlayer *player = [AVAudioPlayer alloc];
because you are left with an uninitialized object and you may happen to just forget to init it later. In normal circumstances you are supposed to always send -init... method to a newly allocated object. In other words, the proper way is to call +alloc and -init in one "sentence".
Second, if AVAudioPlayer's -initWithData:error: returns nil you cannot release it because you send -autorelease message to nil. By convention, if an object cannot be inited, it should release itself and return nil. That's what AVAudioPlayer does if it cannot read or recognize the audio data. In other words, don't bother trying to release it yourself.
Thus, a simplified version of your method goes like this:
- (AVAudioPlayer *)audioPlayerWithContentsOfFile:(NSString *) path {
NSData *audioData = [NSData dataWithContentsOfFile: path];
return [[[AVAudioPlayer alloc] initWithData:audioData error: NULL] autorelease];
}
Make sure you send the message to the proper class because AVAudioPlayer does not implement your -audioPlayerWithContentsOfFile: method. Look where its definition and implementation go.
@interface SomeClass : NSObject
...
- (AVAudioPlayer *)audioPlayerWithContentsOfFile:(NSString *) path;
@end
The above defines -audioPlayerWithContentsOfFile: for instances of class SomeClass, so you should send the message to instances of SomeClass, not AVAudioPlayer. If you'd like to define this method for AVAudioPlayer class, do this instead:
@interface AVAudioPlayer (MyExtensions)
+ (AVAudioPlayer *)audioPlayerWithContentsOfFile:(NSString *) path;
@end
@implementation AVAudioPlayer (MyExtensions)
+ (AVAudioPlayer *)audioPlayerWithContentsOfFile:(NSString *) path {
NSData *audioData = [NSData dataWithContentsOfFile: path];
return [[[AVAudioPlayer alloc] initWithData:audioData error: NULL] autorelease];
}
@end
To use it:
[AVAudioPlayer audioPlayerWithContentsOfFile: [[NSBundle mainBundle] pathForResource: @"knock_knock" ofType:@"wav"]];
Don't compose paths like that. Use NSBundle's
pathForResource:ofType:
or similar.Don't split
+alloc
from-init
(or any other designated initializer). This is a style issue and the standard would be[[AVAudioPlayer alloc] initWithData: foo error: &bar]
.Handle the error and print it out if something goes wrong. It'll typically offer clues.
Now, on to your actual error.
AVAudioPlayer may not response to '-audioPlayerWithContentsOfFile:'
This indicates that you are trying to send the method call to an instance of the AVAudioPlayer
class. Is that intended? I.e. is your .m file the implementation of the class AVAudioPlayer
?
Your code looks a bit funky. You have an instance variable named audioPlayer
, but nowhere do I see anything that assigns to it.
Also, make sure that the header file containing the method declaration is imported into your .m. And make sure you are messaging an instance, not the class.
精彩评论