Xcode's "build and analyze" reports odd memory leaks for this code
Does anyone know why xCode's "build and analyze" would report this line as a "possible memory leak"?
goodSound = [[SoundClass alloc] initializeSound:@"Good.wav"];
///// Here are the 4 files in question:
// Sounds.h
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
@interface SoundClass : NSObject
{
SystemSoundID soundHandle;
}
-(id) initializeSound:(NSString *)soundFileName;
-(void) play;
@end
/////////////
// Sounds.m
#import "Sounds.h"
@implementation SoundClass
-(id) initializeSound:(NSString *)soundFileName
{
self = [super init];
NSString *const resourceDir = [[NSBundle mainBundle] resourcePath];
NSString *const fullPath = [resourceDir stringByAppendingPathComponent:soundFileName];
NSURL *const url = [NSURL fileURLWithPath:fullPath];
OSStatus errCode = AudioServicesCreateSystemSoundID((CFURLRef) url, &soundHandle);
if(errCode == 0)
NSLog(@"Loaded sound: %@", soundFileName);
else
NSLog(@"Failed to load sound: %@", soundFileName);
return self;
}
//////////////////////////////
-(void) play
{
AudioServicesPlaySystemSound(sound开发者_高级运维Handle);
}
/////////////////////////////
-(void) dealloc
{
AudioServicesDisposeSystemSoundID(soundHandle);
[super dealloc];
}
/////////////////////////////
@end
//////////////
// MemTestViewController.h
#import <UIKit/UIKit.h>
@class SoundClass;
@interface MemTestViewController : UIViewController
{
SoundClass *goodSound;
}
-(IBAction) beepButtonClicked:(id)sender;
@end
///////////
// MemTestViewController.m
#import "MemTestViewController.h"
#import "Sounds.h"
@implementation MemTestViewController
- (void)viewDidLoad
{
NSLog(@"view did load: alloc'ing mem for sound class");
// "build and analyze" says this is possibly a memory leak:
goodSound = [[SoundClass alloc] initializeSound:@"Good.wav"];
[super viewDidLoad];
}
-(IBAction) beepButtonClicked:(id)sender
{
NSLog(@"beep button clicked");
[goodSound play];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)dealloc
{
[goodSound release];
[super dealloc];
}
@end
It is a possible memory leak. viewDidLoad
may be called any number of times if the view is unloaded, in which case you will leak memory each time. To guard, you can either release the memory in viewDidUnload
and set the ivar to nil, or you can simply initialize the sound in viewDidLoad
only if the ivar actually is nil.
So:
- (void)viewDidLoad
{
if( !goodSound )
goodSound = [[SoundClass alloc] initializeSound:@"Good.wav"];
[super viewDidLoad];
}
And/or:
- (void)viewDidUnload
{
[goodSound release];
goodSound = nil;
[super viewDidUnload];
}
The static analyzer is right; your code is a possible memory leak. Because static analysis can't prove that MemTestViewController
's dealloc
method will always be called (doing so would solve the halting problem, afterall), there is no way for the static analyzer to be sure that goodSound
will be correctly released.
Modern Objective-C rarely uses direct ivar access accept in init
and dealloc
methods. You should declare a @property (retain)
for your goodSound
, even if it's private, and use that elsewhere in your class, including in the viewDidLoad
method. The static analyzer should then correctly recognize this pattern and not flag the line as a warning.
On a side note—just in case you're interested—your -[SoundClass initializeSound:]
should be named something like -[SoundClass initWithSoundFileName:]
to match convention, and should be written with a guard to test for a nil
return from [super init]
:
-(id)initWithSoundFileName:(NSString*)soundFileName
{
self = [super init];
if(self != nil) {
NSString *const resourceDir = [[NSBundle mainBundle] resourcePath];
NSString *const fullPath = [resourceDir stringByAppendingPathComponent:soundFileName];
NSURL *const url = [NSURL fileURLWithPath:fullPath];
OSStatus errCode = AudioServicesCreateSystemSoundID((CFURLRef) url, &soundHandle);
if(errCode == 0)
NSLog(@"Loaded sound: %@", soundFileName);
else
NSLog(@"Failed to load sound: %@", soundFileName);
}
return self;
}
It's always best to add this guard, especially because you are calling into C code that might not handle nil/NULL
as nicely as Objective-C.
精彩评论