Por que os delegados do Objective-C geralmente recebem a propriedade atribuída em vez de retida?

176

Estou navegando no maravilhoso blog mantido por Scott Stevenson e estou tentando entender um conceito fundamental do Objective-C de atribuir delegados a propriedade 'assign' property vs 'reter'. Observe que os dois são iguais em um ambiente de coleta de lixo. Estou principalmente preocupado com um ambiente não baseado em GC (por exemplo, iPhone).

Diretamente do blog de Scott:

"A palavra-chave assign irá gerar um setter que atribui o valor diretamente à variável de instância, em vez de copiá-lo ou retê-lo. Isso é melhor para tipos primitivos como NSInteger e CGFloat, ou objetos que você não possui diretamente, como delegados".

O que significa que você não é o proprietário direto do objeto delegado? Normalmente retiro meus delegados, porque, se não quero que eles desapareçam no abismo, reter cuidará disso para mim. Normalmente, abstraio o UITableViewController do seu respectivo dataSource e também delego. Também guardo esse objeto em particular. Quero garantir que ele nunca desapareça, para que meu UITableView sempre tenha seu representante por perto.

Alguém pode explicar melhor onde / por que estou errado, para entender esse paradigma comum na programação do Objective-C 2.0 de usar a propriedade assign nos delegados em vez de reter?

Obrigado!

Plumenator
fonte
Marcado com "delegados" e sem "iphone".
Quinn Taylor
por que é delegado atribuir em vez de cópia (como NSString?)
omgpop

Respostas:

175

O motivo de evitar reter delegados é que você precisa evitar um ciclo de retenção:

A cria B A se define como delegado de B ... A é liberado por seu proprietário

Se B tivesse retido A, A não seria liberado, pois B é o dono de A, portanto, o acordo de A nunca seria pago, causando vazamento de A e B.

Você não deve se preocupar com o desaparecimento de A porque ele possui B e, portanto, se livra dele em desalocação.

Andrew Pouliot
fonte
Eu não concordo, Mike. Acabei de encontrar um problema em que um modal tem um delegado que dispensa o modal. Mas quando eu faço um aviso de memória no modal, ele libera o delegado. Então, quando vou dispensar meu modal, o delegado é nulo. Batida.
Paul Shapiro
Ok, então eu discordo, mas você está certo de que é uma falha de design. Eu descobri que estava passando minha chamada de dispensa através de uma classe infantil do real dispensador. Essa classe filho foi liberada e não pôde passar para o delegado do contêiner do modal para dispensar. Eu mudei para passar um ponteiro para o delegado final e esse não foi liberado com aviso de memória e está tudo bem.
Paul Shapiro
2
Seu código nunca deve ser escrito de forma que um delegado nulo cause uma falha. Somente o objeto proprietário deve ter uma referência de propriedade. Quando desalocado, ele deve definir os delegados de objetos de propriedade como nulos antes de liberá-los. Em seguida, todas as mensagens enviadas para o delegado nulo são simplesmente ignoradas. Passar um objeto nulo em uma mensagem pode falhar, no entanto. Apenas certifique-se de não lidar com os delegados dessa maneira.
David Gish
Espere - não é isso que weakfaz? A questão é por que usar em assignvez de weak?
Wcochran
3
@ wcochran: Não, esta questão é por que usar em assignvez de retain. A questão é mais antiga que o ARC; weake strong(o último sendo sinônimo de retain) não existia até o ARC ser introduzido. Você deve fazer sua pergunta sobre weakvs. assignseparadamente.
Peter Hosey
44

Porque o objeto que envia as mensagens de delegado não é o proprietário do delegado.

Muitas vezes, é o contrário, como quando um controlador se define como o delegado de uma visão ou janela: o controlador é o proprietário da visão / janela; portanto, se a visão / janela possui o seu delegado, os dois objetos se pertencem. É claro que este é um ciclo de retenção, semelhante a um vazamento com a mesma consequência (objetos que deveriam estar mortos permanecem vivos).

Outras vezes, os objetos são pares: nenhum deles é dono do outro, provavelmente porque ambos pertencem ao mesmo terceiro objeto.

De qualquer forma, o objeto com o delegado não deve reter seu delegado.

(A propósito, há pelo menos uma exceção. Não me lembro o que era e acho que não havia uma boa razão para isso.)


Adendo (adicionado 19/05/2012): No ARC, você deve usar em weakvez de assign. Referências fracas são definidas nilautomaticamente quando o objeto morre, eliminando a possibilidade de o objeto delegador acabar enviando mensagens para o delegado morto.

Se você estiver longe do ARC, por algum motivo, altere pelo menos as assignpropriedades que apontam para objetos unsafe_unretained, o que torna explícito que essa é uma referência não retida, mas não zero, a um objeto.

assign permanece apropriado para valores não-objeto tanto no ARC quanto no MRC.

Peter Hosey
fonte
13
NSURLConnectionmantém seu delegado.
Sim, use weak, mas isso não responde à pergunta original: por que a Apple usa em assignvez de weak?
Wcochran
@wcochran: A pergunta original era "por que as propriedades delegadas são dadas em assignvez de retidas "; weaknão existia quando foi solicitado. Sua pergunta é diferente, e você deve perguntar separadamente. Eu ficaria feliz em responder.
Peter Hosey
@wcochran e Peter, essa pergunta foi feita em outro lugar?
Rogers,
17

Observe que, quando você tem um delegado designado, é muito importante sempre definir esse valor de delegado para zero quando o objeto for desalocado - portanto, um objeto deve sempre ter o cuidado de anular as referências de delegado no desalocamento, se não tiver. feito em outro lugar.

Kendall Helmstetter Gelner
fonte
“Observe que, quando você tem um delegado que é designado, é muito importante sempre definir esse valor de delegado para zero sempre que o objeto for desalocado” Por que?
31511 Peter Hosey
2
Como qualquer referência deixada definida, será inválida depois que um objeto for desalocado (apontando para a memória que não está mais alocada para o tipo de objeto esperado) - e, portanto, causará uma falha se você tentar usá-lo. Um sinal disso no depurador é quando o depurador afirma que alguma variável tem um tipo que parece totalmente errado em relação ao que a variável é realmente declarada.
Kendall Helmstetter Gelner
1
Isso é necessário apenas se o objeto do qual você é um representante estiver sendo retido por outra fonte, como um timer ou outro retorno de chamada assíncrono. Caso contrário, ele será desalocado depois que você o liberar e não tentará chamar métodos de delegação.
Andrew Pouliot
@ Andrew: Isso é verdade, mas se você sempre pratica a falta de delegados, não se esquece quando isso importa, ou se você retém acidentalmente um objeto retido e ele permanece por aí de qualquer maneira. Se você deletar o delegado, o resultado será apenas um vazamento, em vez de um vazamento seguido de uma falha.
Kendall Helmstetter Gelner
1

Uma das razões por trás disso é evitar ciclos de retenção. Apenas para evitar o cenário em que ambos os objetos A e B se referem um ao outro e nenhum deles é liberado da memória.

A atribuição aguda é melhor para tipos primitivos como NSInteger e CGFloat, ou objetos que você não possui diretamente, como delegados.

Puneet Sharma
fonte
Isso é bastante copiado da citação do OP e da resposta aceita, respectivamente, não é?
dakab