你必须原谅我,因为我还是比较新的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信号..
为什么?
我分配了内存,我也不应该释放它吗?
我该如何释放它?
谢谢你的支持!
答案 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];
}