简体   繁体   中英

Obj-C iPhone: Memory Management

iPhone beginner here, I am creating a simple music iPhone application called Piano Master, that produces a sound when a button is clicked.

Here is my code:

MusicViewController.h

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "PianoMasterAppDelegate.h"

@interface MusicViewController : UIViewController
<AVAudioPlayerDelegate> {}

- (IBAction)buttonClick:(id)sender;

@end

MusicViewController.m

#import "MusicViewController.h"

@implementation MusicViewController

- (IBAction)buttonClick:(id)sender
{
      NSString *path = [[NSBundle mainBundle] pathForResource:@"Piano1" ofType:@"wav"];
      AVAudioPlayer *theAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL    fileURLWithPath:path error:NULL];
      theAudio.delegate = self;

      [theAudio play];
}

The sound plays when the button is clicked, but everytime the button is clicked, a new AVAudioPlayer is created, what are your best ideas on how I can manage this memory problem efficiently? I've thought of making an instance of AVAudioPlayer for the MusicViewController and using that, but there will still be a memory problem if I keep allocating a new AVAudioPlayer each time... Any help is useful, thanks.

Retain your audio players in another controller (MVC) object somewhere after initializing them. Then just reuse existing ones by calling the audio controller object from the view controller.

hotpaw2's answer is what i'd suggest doing.

But a key thing you're missing is memory management. You should add:

[theAudio play];
[theAudio release];

What you allocate in memory you should release. So even though you're creating an AVAudioPlayer each time. You'll be releasing the memory used. So you don't get any leaks.

MusicViewController.h file

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "PianoMasterAppDelegate.h"

@interface MusicViewController : UIViewController <AVAudioPlayerDelegate> {
    AVAudioPlayer* _theAudio;
}

- (IBAction)buttonClick: (id)sender;

@end

MusicViewController.m file

#import "MusicViewController.h"

@implementation MusicViewController

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

- (AVAudioPlayer*)theAudio {
    if (_theAudio == nil) {
        NSString* path = [[NSBundle mainBundle] pathForResource: @"Piano1" ofType: @"wav"];
        _theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL: [NSURL fileURLWithPath: path error: nil];
        [_theAudio setDelegate: self];
    }
    return _theAudio;
}

- (IBAction)buttonClick: (id)sender {
    [self.theAudio play];
}

You might also want to release theAudio in the viewDidUnload method to free up memory, make sure you set the pointer to nil afterword like this:

- (void)viewDidUnload {
    [_theAudio release];
    _theAudio = nil;
}

Assuming you'll want to play more than a couple different audio files, a dictionary of AVAudioPlayers would work well. For example, the first time a sound file is requested, create the AVAudioPlayer object and put it in your sounds dictionary, then for each subsequent request, just grab the existing AVAudioPlayer object from the dictionary.

Here's a simple implementation:

@interface MusicViewController : UIViewController <AVAudioPlayerDelegate> {
    NSMutableDictionary *sounds;
}
- (IBAction)buttonClick:(id)sender;
- (AVAudioPlayer *)playerForSoundFile:(NSString *)fileName;
@end


@implementation MusicViewController

- (id)init
{
    if (! (self = [super init]))
        return nil;

    sounds = [[NSMutableDictionary alloc] init];

    return self;
}

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

- (AVAudioPlayer *)playerForSoundFile:(NSString *)fileName
{

    AVAudioPlayer *player = [sounds objectForKey:fileName];

    if (! player) {
        NSString *path = [[NSBundle mainBundle] pathForResource:fileName 
                                                         ofType:@"wav"];
        NSURL *url = [NSURL fileURLWithPath:path];
        player = [[[AVAudioPlayer alloc] initWithContentsOfURL:url] autorelease];
        player.delegate = self;

        [sounds setObject:player forKey:fileName];
    }

    return player;
}

- (IBAction)buttonClick:(id)sender
{
    AVAudioPlayer *theAudio = [self playerForSoundFile:@"Piano1"];
    [theAudio play];
}

@end

Note that the AVAudioPlayer is "autoreleased", then added to the 'sounds' dictionary. This means that the when the dictionary is destroyed, all of the AVAudioPlayers will be as well. If this isn't clear, you should read up on memory management in Objective-C: http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/MemoryMgmt/MemoryMgmt.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM