Normalmente, os ouvintes de eventos não devem sobreviver ao objeto que os registrou.
Isso significa que os ouvintes de eventos devem ser mantidos por referências fracas por padrão (armazenadas em coleções fracas pelos objetos em que os ouvintes estão registrados)?
Existem casos válidos em que o ouvinte deve sobreviver ao seu criador?
Ou talvez uma situação como essa seja um erro e não deva ser permitida?
api-design
event
mrpyo
fonte
fonte
Respostas:
Por que os ouvintes de eventos não devem sobreviver ao objeto que os registrou? Parece que você está assumindo que os ouvintes de eventos devem ser registrados por métodos de controle (se usarmos o exemplo da GUI) - ou mais precisamente, métodos por objetos de classes que herdam os controles do kit de ferramentas da GUI. Isso não é necessário - você pode, por exemplo, usar um objeto especializado para registrar ouvintes de eventos e abandonar esse objeto posteriormente.
Além disso, se os ouvintes de evento fossem fracamente referenciados, você teria que realmente manter referências a eles, mesmo que nunca use essa referência. Caso contrário, o ouvinte será coletado aleatoriamente. Então, nós temos um bug que é
E se evitar esse bug não é um incentivo suficiente, aqui estão mais alguns:
Você terá que pensar em um nome para cada ouvinte que criar.
Alguns idiomas usam análises estáticas que geram um aviso se você tiver um campo de membro privado que nunca seja escrito ou lido. Você precisará usar um mecanismo para substituir isso.
O ouvinte de evento faz alguma coisa e, uma vez que o objeto que possui uma referência forte é coletado, ele pára de fazer isso. Agora você tem algo que afeta o estado do programa e depende do GC - o que significa que o GC afeta o estado concreto do programa. E isso é ruim !
O tratamento de referências fracas é mais lento, pois você tem outro nível de indireção e precisa verificar se a referência foi coletada. Isso não seria um problema se fosse necessário ter ouvintes de eventos em referências fracas - mas não é!
fonte
Em geral, sim, referências fracas devem ser usadas. Mas primeiro precisamos esclarecer o que você quer dizer com "ouvintes de eventos".
Retornos de chamada
Em alguns estilos de programação, especialmente no contexto de operações assíncronas, é comum representar uma parte de um cálculo como um retorno de chamada que é executado em um determinado evento. Por exemplo, um
Promise
[ 1 ] pode ter umthen
método que registra um retorno de chamada após a conclusão da etapa anterior:Aqui, os retornos de chamada registrados por
then
devem ser mantidos por fortes referências, pois a promessa (a fonte do evento) é o único objeto que mantém uma referência ao retorno de chamada. Isso não é um problema, pois a promessa em si tem uma vida útil limitada e será coletada como lixo após a conclusão da cadeia de promessas.Padrão do observador
No padrão do observador, um sujeito tem uma lista de observadores dependentes. Quando o sujeito entra em algum estado, os observadores são notificados de acordo com alguma interface. Os observadores podem ser adicionados e removidos do assunto. Esses observadores não existem no vácuo semântico, mas estão aguardando eventos para algum propósito.
Se esse objetivo não existir mais, os observadores devem ser removidos do assunto. Mesmo em idiomas coletados pelo lixo, essa remoção pode ter que ser realizada manualmente. Se não conseguirmos remover um observador, ele será mantido vivo por meio da referência do sujeito ao observador, e com ele todos os objetos que o observador fizer referência. Isso desperdiça memória e prejudica o desempenho, pois o observador (agora inútil) ainda será notificado.
Referências fracas corrigem esse vazamento de memória, pois permitem que o observador seja coletado como lixo. Quando o assunto avisa todos os observadores e descobre que uma das referências fracas a um observador está vazia, essa referência pode ser removida com segurança. Como alternativa, as referências fracas podem ser implementadas de uma maneira que permita ao sujeito registrar um retorno de chamada de limpeza que removerá o observador na coleta.
Mas observe que referências fracas são apenas um curativo que limitam o dano ao esquecer de remover um observador. A solução correta seria garantir que um observador seja removido quando não for mais necessário. As opções incluem:
Fazê-lo manualmente, mas isso é propenso a erros.
Usando algo semelhante à tentativa de recurso em Java ou
using
em C #.Destruição determinística, como através do idioma RAII. Observe que em um idioma com coleta de lixo determinística, isso ainda pode exigir referências fracas do sujeito para o observador, a fim de acionar o destruidor.
fonte