KVO e ARC como removerObserver


87

Como você remove um observador de um objeto sob ARC ? Nós apenas adicionamos o observador e esquecemos de removê-lo? Se não gerenciamos mais a memória manualmente, onde renunciamos à observação?

Por exemplo, em um controlador de visualização:

[self.view addObserver:self
            forKeyPath:@"self.frame"
               options:NSKeyValueObservingOptionNew 
               context:nil];

Anteriormente, eu chamaria removeObserver:o deallocmétodo do controlador de visualização .


4
Observe que é uma péssima ideia KVO .frame. Como escrito em outro lugar pelos engenheiros da Apple no StackOverflow, a propriedade frame do UIKit não é compatível com KVO. Quando funciona, é apenas por acaso.
steipete

2
Seu keyPath não deveria ser em @"frame"vez de @"self.frame"?
Besi de

Respostas:


126

Você ainda pode implementar -deallocsob ARC, que parece ser o local apropriado para remover a observação dos valores-chave. Você simplesmente não chama mais [super dealloc]de dentro deste método.

Se você estava substituindo -releaseantes, você estava fazendo as coisas da maneira errada.


1
Você tem certeza disso? Cito clang.llvm.org/docs/… , seção 7.1.2. dealloc: "Justificativa: embora o ARC destrua variáveis ​​de instância automaticamente, ainda existem razões legítimas para escrever um método dealloc, como liberar recursos não retíveis. Deixar de chamar [super dealloc] em tal método é quase sempre um bug."
Elise van Looij

@ElisevanLooij Sim, é verdade. Se você deriva desta classe, parece óbvio que você deve chamar [super dealloc]. Quem mais deveria fazer isso por você.
Björn Landmesser

@ElisevanLooij Oops, bem, deveria ter verificado antes. Não é permitido chamar [super dealloc]um método dealloc. Não tenho ideia de como isso funcionaria, então, ao criar uma subclasse da classe mencionada. Talvez seja apenas aconselhável usar em finalizevez disso (para onde você ligar [super finalize])
Björn Landmesser

17
@ElisevanLooij - O ponto que eles estavam tentando fazer é em relação ao caso de gerenciamento manual de memória. Como não chamar por [super dealloc]último nesse método é quase sempre um bug no gerenciamento de memória manual, o compilador trata disso para você agora, e é por isso que você não pode mais chamar -deallocdiretamente. As únicas coisas que você insere em um -deallocmétodo sob ARC são quaisquer recursos não-objeto que você precisa liberar ou tarefas de limpeza, como remover observadores. O texto que eles usam é um pouco confuso, mas é isso que eles querem dizer.
Brad Larson

7
@ BjörnMilcke - Como comento a resposta de Elise, -finalizeé usado para isso em coleta de lixo, onde -deallocnunca é chamado, mas é perfeitamente aceitável colocar esse código em -deallocARC. [super dealloc]é chamado automaticamente, por isso é um erro chamá-lo no ARC.
Brad Larson

1

Eu faço isso com este código

- (void)dealloc
{
@try{
    [self.uAvatarImage removeObserver:self forKeyPath:@"image" context:nil];
} @catch(id anException) {
    //do nothing, obviously it wasn't attached because an exception was thrown
}
}    

2
Qual é o ponto de tratamento da exceção dealloc? É tarde demais para fazer algo a respeito.
Abizern

Qual é o ponto de remover observadores em uma variável de instância no dealloc? Este uAvatarImage será desalocado em breve, juntamente com todos os observadores inscritos em seus caminhos de chave.
shoumikhin de

1
@shoumikhin Estou usando ARC e tive que remover o observador no método dealloc. Eu tenho a mesma pergunta que você. No entanto, quando executei várias instâncias da classe, eventualmente, obtive o erro exc_bad_address. Fazer isso resolveu o problema. Além disso, a resposta aqui stackoverflow.com/questions/32490808/… me ajudou a descobrir o problema.
mac10688

-2

Em outro lugar no estouro de pilha, Chris Hanson aconselha o uso do método finalize para esse propósito e a implementação de um método invalidate separado para que os proprietários possam dizer aos objetos que eles terminaram. No passado, eu descobri que as soluções do Hanson eram bem pensadas, então irei com isso.


13
Observe que ele estava se referindo à coleta de lixo lá, não ao ARC (sua resposta foi escrita em 2008). Na coleta de lixo, -deallocnunca é chamado. No ARC, é. É perfeitamente aceitável remover os observadores KVO -dealloc, como Chris Lattner (quem sabe do que está falando) indica nos fóruns de desenvolvedores da Apple aqui: devforums.apple.com/message/475850
Brad Larson

3
Obrigado Brad, por fazer todo esse trabalho. Não para finalizar, sim para desalocar, mas sem [super desalocar]. Realmente simples, uma vez que você saiba. Ei, @drunknbass, aceite a resposta daquele homem!
Elise van Looij
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.