【问题标题】:Why is this object release not OK, should i be releasing it?为什么这个对象释放不正常,我应该释放它吗?
【发布时间】:2023-03-03 01:32:02
【问题描述】:

您必须原谅我,因为我对 Obj-C 还很陌生,但我很困惑..

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

当用户点击按钮时,我在 player 变量上调用 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 信号..

为什么?

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

我应该如何释放它?

提前感谢!

【问题讨论】:

  • Objective-C 具有向 nil 对象发送消息而不会导致应用程序崩溃的便捷功能。这就是它第一次起作用的原因。不过,当你第一次遇到它时,它有点奇怪。
  • 您在哪一行得到EXC_BAD_ACCESS
  • +1 关于塞尔吉奥的问题,还有你是如何宣布球员的?它是保留财产吗?
  • 我在[player release]; 上收到了EXC_BAD_ACCESS
  • 啊,我的声明可能有问题:@interface SoundboardViewController : UIViewController { AVAudioPlayer *player; } 抱歉!从 C# 和 ruby​​ 的背景来看,我还是很陌生!

标签: objective-c ios memory pointers avaudioplayer


【解决方案1】:

所以我会告诉你我会怎么做以及为什么。

在您的.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 不能处理内存管理,具体取决于您是否将 assign/retain/copy 传入@property 行。

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

我现在将您的 -(IBAction)userDidTapButton:(id)sender 更改为如下所示

-(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];
}

每当我与 audioPlayer ivar 交互时,我都会使用 getter/setter。这意味着每次我设置 ivar 时都会进行内存管理(例如,它释放旧播放器并保留新播放器)。之所以使用 getters/setters,是因为 self.audioPlayer 将被编译为适当的调用,如下所示:

self.audioPlayer;             // compiled to -> [self audioPlayer];
self.audioPlayer = tmpPlayer; // compiled to -> [self setAudioPlayer:tmpPlayer];

现在要整理并让- (void)dealloc; 方法正确,我们应该直接使用ivar,而不通过getter/setter,所以我必须使用我们合成的_audioPlayer ivar:

#pragma mark - Cleanup

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

【讨论】:

    【解决方案2】:

    我有时也会遇到这些奇怪的问题。 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 是您的类中的一个实例变量。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        但是这感觉就像当我反复调用 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];
        }
        

        【讨论】:

        • 谢谢.. 只是出于兴趣,为什么将发布版移到 *soundClip 声明下方会停止错误?
        猜你喜欢
        • 2011-04-27
        • 1970-01-01
        • 2011-01-17
        • 2010-10-21
        • 2010-12-18
        • 2010-12-03
        • 2011-11-18
        • 2011-07-04
        • 1970-01-01
        相关资源
        最近更新 更多