No Cocoa, preciso impedir que um objeto receba notificações KVO ao desalocá-lo?
Pergunta
Quando eu registrei um objeto foo para receber notificações KVO de outro objeto bar (usando addObserver:...), se eu então desalocar foo preciso enviar um removeObserver:forKeyPath:
mensagem para bar em -dealloc?
Solução
Você precisa usar -removeObserver:forKeyPath:
remover o observador antes -[NSObject dealloc]
corre, então sim, fazendo isso no -dealloc
método da sua classe funcionaria.
Melhor do que isso seria ter um ponto determinístico onde o dono do objeto que está fazendo a observação poderia dizer que está feito e será (eventualmente) desalocado.Dessa forma, você pode parar de observar imediatamente quando o objeto que está observando não for mais necessário, independentemente de quando ele for realmente desalocado.
É importante ter isso em mente porque o tempo de vida dos objetos no Cocoa não é tão determinístico quanto algumas pessoas parecem pensar.As diversas estruturas do Mac OS X vai envie seus objetos -retain
e -autorelease
, estendendo sua vida útil além do que você poderia imaginar que seria.
Além disso, ao fazer a transição para a coleta de lixo do Objective-C, você descobrirá que -finalize
será executado em momentos muito diferentes - e em contextos muito diferentes - do que -dealloc
fez.Por um lado, a finalização ocorre em um thread diferente, então você realmente não pode enviar com segurança -removeObserver:forKeyPath:
para outro objeto em um -finalize
método.
Atenha-se ao gerenciamento de memória (e outros recursos escassos) em -dealloc
e -finalize
, e use um separado -invalidate
método para fazer com que um proprietário diga a um objeto que você terminou com ele em um ponto determinístico;faça coisas como remover observações KVO lá.A intenção do seu código ficará mais clara e você terá menos bugs sutis para resolver.
Outras dicas
Um pouco de informação extra que ganhei com uma experiência dolorosa:embora o NSNotificationCenter use zerar referências fracas ao executar na coleta de lixo, o KVO não o faz.Assim, você pode não remover um observador NSNotificationCenter ao usar o GC (ao usar reter/liberar, você ainda precisa remover seu observador), mas ainda deve remover seus observadores KVO, como Chris descreve.
Definitivamente concordo com Chris no comentário "Atenha-se ao gerenciamento de memória (e outros recursos escassos) em -dealloc e -finalize...".Muitas vezes vejo pessoas tentando invalidar objetos NSTimer em suas funções dealloc.O problema é que o NSTimer mantém seus alvos.Portanto, se o alvo desse NSTimer for self, dealloc nunca será chamado, resultando em vazamentos de memória potencialmente desagradáveis.
Invalidar em -invalidate
e faça outra limpeza de memória em seu dealloc
e finalize.