为什么这个对象释放不正常,我应该发布吗?

时间:2011-07-27 11:37:03

标签: objective-c ios memory pointers avaudioplayer

你必须原谅我,因为我还是比较新的Obj-C,但我很困惑......

我有一个带有12个按钮的小音板应用程序..每个调用相同的IBAction ..

当用户点击按钮时,我在播放器变量上调用alloc init(在类的接口部分声明)

这一切都很好,花花公子:

#pragma mark - IBActions

-(IBAction)userDidTapButton:(id)sender {
    [player stop];

    NSURL *soundClip = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"clip" ofType:@"mp3"]];

    player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundClip error:nil];
    [player setNumberOfLoops:-1];
    [player play];
}

#pragma mark - Cleanup

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

然而这感觉就像我正在反复调用alloc init时,我正在留下内存悬空(因为我将播放器指针分配给一个新变量而不释放旧的......)

为了解决这个问题,我尝试在IBAction的顶部添加它:

-(IBAction)userDidTapButton:(id)sender {
    [player stop];
    [player release];

    ... etc ...

第一次单击按钮时这是有效的(这对我来说很奇怪,因为它实际上是空指针,因为它尚未分配和初始化(对吗?))但是当我再次点击按钮时它会抛出一个EXC_BAD_ACCESS信号..

为什么?

我分配了内存,我也不应该释放它吗?

我该如何释放它?

谢谢你的支持!

4 个答案:

答案 0 :(得分:3)

所以我会告诉你我将如何做以及为什么。

在您的.h文件中使用此类属性声明player ivar

// .h
@interface MyClass : UIViewController

@property (nonatomic, retain) AVAudioPlayer *audioPlayer;

// method signatures

@end

我将其命名为audioPlayer只是为了更明确(​​这是个人偏好)。

在您的实施文件中,您需要synthesize这样的ivar

// .m
@implementation MyClass

@synthesize audioPlayer = _audioPlayer;

// Do some stuff

@end

这将创建支持ivar以及带有签名- (void)setAudioPlayer:(AVAudioPlayer *)audioPlayer- (AVAudioPlayer *)audioPlayer;的getter和setter,但在后台他们将操纵ivar _audioPlayer

你在回复中提到你来自Ruby,这可以比作像attr_accessor :audio_player这样的东西但是在Objective-C中它创建了setter和getter而不是处理内存管理,这取决于你是否传入{{ 1}}进入assign/retain/copy行。

Apple在大多数示例中都是这样做的,这意味着当您直接访问ivar或通过getter / setter时,它会更清晰。

我现在将您的@property更改为

-(IBAction)userDidTapButton:(id)sender

我在使用audioPlayer ivar的任何时候都使用过getters / setter。这意味着每次设置ivar时都会处理内存管理(例如,它会释放旧播放器并保留新播放器)。这是使用getter / setter的原因是因为-(IBAction)userDidTapButton:(id)sender { [self.audioPlayer stop]; NSURL *soundClip = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"clip" ofType:@"mp3"]]; AVAudioPlayer *tmpPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundClip error:nil];; self.audioPlayer = tmpPlayer; [tmpPlayer release]; tmpPlayer = nil; [self.audioPlayer setNumberOfLoops:-1]; [self.audioPlayer play]; } 将被编译为适当的调用,如下所示:

self.audioPlayer

现在要整理并使self.audioPlayer; // compiled to -> [self audioPlayer]; self.audioPlayer = tmpPlayer; // compiled to -> [self setAudioPlayer:tmpPlayer]; 方法正确,我们应该直接使用ivar而不通过getter / setter,所以我必须使用我们合成的- (void)dealloc; ivar:

_audioPlayer

答案 1 :(得分:0)

我有时也会遇到这些奇怪的问题。进入Objective-C代码的一个好习惯是与所有已分配对象相同的模式:调用alloc init,执行某些操作(包括保留它),然后释放。我发现如果你用相同的方法做所有事情就可以预测。

因此,在您的情况下,请尝试以下方法:

-(IBAction)userDidTapButton:(id)sender {

    [myPlayer stop];
    [myPlayer release];
    NSURL *soundClip = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"clip" ofType:@"mp3"]];

    AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundClip error:nil];
    [player setNumberOfLoops:-1];
    [player play];
    myPlayer = [player retain];
    [player release];
}

其中myPlayer是您班级中的实例变量。

答案 2 :(得分:0)

您应该在创建新播放器之前释放前一个播放器,或者只重复使用之前创建的播放器。所以在[玩家停止]之后;添加[播放器发布]; player = nil; = nil;是的,你可以安全地发送dealloc方法发布。你也应该加一个[玩家停止];在你[播放器发布]之前;在你dealloc方法。如果没有太多按钮,您可能还想为每个按钮保留一个AVAudioPlayer实例。

答案 3 :(得分:0)

  

然而,当我正在反复调用alloc init时,感觉就像我正在留下记忆danglin

是的,你是。您应该在分配新播放器之前释放旧播放器。

-(IBAction)userDidTapButton:(id)sender {
    [player stop];

    NSURL *soundClip = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"clip" ofType:@"mp3"]];

    [player release]; // <<<=== this needs to be here

    player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundClip error:nil];
    [player setNumberOfLoops:-1];
    [player play];
}

但是,最好为玩家创建一个属性来处理所有这些:

@interface MyClass : WhateverSuperClass
{
@private
    // other ivars

    AVAudioPlayer* player
}

@property (retain) AVAudioPlayer* player;

// other methods

@end

@implementation MyClass

@synthesize player;

// other stuff

-(IBAction)userDidTapButton:(id)sender {
    [[self player] stop];

    NSURL *soundClip = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"clip" ofType:@"mp3"]];

    [self setPlayer: [[[AVAudioPlayer alloc] initWithContentsOfURL:soundClip error:nil] autorelease]];
    [[self player] setNumberOfLoops:-1];
    [[self player] play];
}

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