Почему этот релиз объекта не в порядке, я должен выпускать его? - PullRequest
2 голосов
/ 27 июля 2011

Вы должны простить меня, потому что я все еще довольно новичок в Obj-C, но я в замешательстве ..

У меня есть это небольшое приложение для звуковой платы с 12 кнопками ... каждая из которых вызываеттот же IBAction ..

Когда пользователь нажимает кнопку, я вызываю alloc init для переменной player (которая объявлена ​​в интерфейсной части класса)

Это работает отлично и прекрасно:

#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 ]

3 голосов
/ 27 июля 2011

Итак, я расскажу вам, как и почему.

В вашем файле .h объявите player ivar с таким свойством

// .h
@interface MyClass : UIViewController

@property (nonatomic, retain) AVAudioPlayer *audioPlayer;

// method signatures

@end

Я назвал его audioPlayer, просто чтобы быть более точным (это личное предпочтение).

В вашем файле реализации вам нужно synthesize этот ивар как этот

// .m
@implementation MyClass

@synthesize audioPlayer = _audioPlayer;

// Do some stuff

@end

Это создастподдерживающий ивар и геттер и сеттер с подписями - (void)setAudioPlayer:(AVAudioPlayer *)audioPlayer и - (AVAudioPlayer *)audioPlayer;, но на заднем плане они будут манипулировать иваром _audioPlayer.

Вы упомянули в ответе, что вы пришли из Ruby, это можно сравнить с чем-то вроде этого attr_accessor :audio_player, но в Objective-C он создает сеттеры и геттеры, которые могут иметь дело с управлением памятью в зависимости от того, передаете ли вы assign/retain/copy в строку @property.

Так Apple делает это в большинстве своих примеров, и это означает, что это более понятно, когда вы обращаетесь к ивуру напрямую или проходите через метод получения / установки.

Я бы сейчас изменил ваш -(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.Это означает, что управление памятью осуществляется каждый раз, когда я устанавливаю ivar (например, он освобождает старого игрока и сохраняет нового).Причина, по которой он использует методы получения / установки, заключается в том, что self.audioPlayer будет скомпилирован для соответствующего вызова следующим образом:

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

Теперь, чтобы привести в порядок и сделать правильный метод - (void)dealloc;, мы должныиспользуйте ивар непосредственно, не проходя через геттер / сеттеры, поэтому я должен использовать _audioPlayer ивар, который мы синтезировали следующим образом:

#pragma mark - Cleanup

- (void)dealloc 
{
  [_audioPlayer release];
  [super dealloc];
}
0 голосов
/ 27 июля 2011

Однако это похоже на то, что когда я неоднократно вызываю 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];
}
0 голосов
/ 27 июля 2011

Вы должны выпустить предыдущего игрока, прежде чем создавать нового, или просто повторно использовать ранее созданного игрока. Итак, после [остановки игрока]; добавить [релиз игрока]; игрок = ноль; = ноль; Таким образом, вы можете безопасно отправлять релиз в ваши методы dealloc. Вы также должны добавить [стоп игрока]; перед вами [релиз игрока]; в тебе дело деллок. Вы также можете сохранить экземпляр AVAudioPlayer для каждой кнопки, если их не слишком много.

0 голосов
/ 27 июля 2011

Я иногда тоже получаю эти странные проблемы.Хорошая привычка использовать код 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переменная экземпляра в вашем классе.

...