Receio que essa pergunta seja bastante básica, mas acho relevante para muitos programadores de Objective-C que estão entrando em blocos.
O que ouvi é que, como os blocos capturam variáveis locais referenciadas como const
cópias, o uso self
dentro de um bloco pode resultar em um ciclo de retenção, caso esse bloco seja copiado. Portanto, devemos usar __block
para forçar o bloco a lidar diretamente, em self
vez de copiá-lo.
__block typeof(self) bself = self;
[someObject messageWithBlock:^{ [bself doSomething]; }];
em vez de apenas
[someObject messageWithBlock:^{ [self doSomething]; }];
O que eu gostaria de saber é o seguinte: se isso for verdade, existe uma maneira de evitar a feiura (além de usar o GC)?
objective-c
memory-management
objective-c-blocks
Jonathan Sterling
fonte
fonte
self
proxiesthis
apenas para mudar as coisas. Em JavaScript, chamo meusthis
encerramentosself
, para que pareça agradável e equilibrado. :)Respostas:
Estritamente falando, o fato de ser uma cópia const não tem nada a ver com esse problema. Os blocos reterão quaisquer valores obj-c capturados quando criados. Acontece que a solução alternativa para o problema de cópia constante é idêntica à solução alternativa para o problema de retenção; ou seja, usando o
__block
classe de armazenamento para a variávelDe qualquer forma, para responder sua pergunta, não há alternativa real aqui. Se você estiver projetando sua própria API baseada em bloco e fizer sentido, poderá fazer com que o bloco passe o valor de
self
in como argumento. Infelizmente, isso não faz sentido para a maioria das APIs.Observe que a referência a um ivar tem exatamente o mesmo problema. Se você precisar fazer referência a um ivar no seu bloco, use uma propriedade ou use
bself->ivar
.Adendo: Ao compilar como ARC, as
__block
quebras não retêm mais ciclos. Se você estiver compilando para o ARC, precisará usar__weak
ou em__unsafe_unretained
vez disso.fonte
__weak
está bem. Se você sabe que o objeto não pode estar fora do escopo quando o bloco é chamado,__unsafe_unretained
é sempre um pouco mais rápido, mas em geral isso não fará diferença. Se você o usar__weak
, certifique-se de jogá-lo em uma__strong
variável local e teste-onil
antes de fazer qualquer coisa com ele.__block
O efeito colateral de não reter e liberar se deveu simplesmente à incapacidade de raciocinar adequadamente sobre isso. Com o ARC, o compilador ganhou essa capacidade e__block
agora retém e libera. Se você precisar evitar isso, precisará usar o__unsafe_unretained
que instrui o compilador a não executar retenções ou liberações no valor da variável.Apenas use:
Para mais informações: WWDC 2011 - Blocos e expedição da Grand Central na prática .
https://developer.apple.com/videos/wwdc/2011/?id=308
Nota: se isso não funcionar, você pode tentar
fonte
Isso pode ser óbvio, mas você só precisa fazer o
self
alias feio quando sabe que terá um ciclo de retenção. Se o bloqueio é apenas uma coisa, acho que você pode ignorar com segurança a retençãoself
. O caso ruim é quando você tem o bloco como uma interface de retorno de chamada, por exemplo. Como aqui:Aqui, a API não faz muito sentido, mas faria sentido ao se comunicar com uma superclasse, por exemplo. Nós retemos o manipulador de buffer, o manipulador de buffer nos mantém. Compare com algo como isto:
Nessas situações, eu não faço o
self
alias. Você obtém um ciclo de retenção, mas a operação tem vida curta e o bloco fica sem memória eventualmente, interrompendo o ciclo. Mas minha experiência com blocos é muito pequena e pode ser que oself
aliasing seja uma prática recomendada a longo prazo.fonte
copy
, nãoretain
. Se eles são justosretain
, então não há garantia de que eles serão removidos da pilha, o que significa que, quando você for executá-lo, ele não estará mais lá. (e copiar e bloco já copiado é otimizado para a reter)retain
fase há um tempo e rapidamente percebi o que você está dizendo :) Obrigado!retain
é completamente ignorado por blocos (a menos que eles já tenham saído da pilhacopy
).Postando outra resposta, porque isso também foi um problema para mim. Originalmente, pensei que tinha que usar blockSelf em qualquer lugar onde houvesse uma auto-referência dentro de um bloco. Não é esse o caso, é apenas quando o próprio objeto possui um bloco. E, de fato, se você usar o blockSelf nesses casos, o objeto poderá ser desalocado antes que você obtenha o resultado de volta do bloco e depois travará quando tentar chamá-lo, tão claramente você deseja que seja retido até a resposta volta.
O primeiro caso demonstra quando um ciclo de retenção ocorrerá porque contém um bloco que é referenciado no bloco:
Você não precisa blockSelf no segundo caso, porque o objeto de chamada não possui um bloco que causará um ciclo de retenção quando você fizer referência a si próprio:
fonte
self
podem não ser devidos às pessoas que aplicam essa correção em excesso. Este é um bom exemplo de como evitar ciclos de retenção em código não-ARC, obrigado pela postagem.Lembre-se também de que podem ocorrer ciclos de retenção se o seu bloco se referir a outro objeto que então retém
self
.Não tenho certeza de que a coleta de lixo possa ajudar nesses ciclos de retenção. Se o objeto que retém o bloco (que chamarei de objeto servidor) sobrevive
self
(o objeto cliente), a referência aself
dentro do bloco não será considerada cíclica até que o próprio objeto retentor seja liberado. Se o objeto do servidor sobreviver a seus clientes, você pode ter um vazamento de memória significativo.Como não há soluções limpas, eu recomendaria as seguintes soluções alternativas. Sinta-se à vontade para escolher um ou mais deles para corrigir seu problema.
doSomethingAndWhenDoneExecuteThisBlock:
, e não métodos comosetNotificationHandlerBlock:
. Os blocos usados para conclusão têm fins de vida definidos e devem ser liberados pelos objetos do servidor após serem avaliados. Isso impede que o ciclo de retenção viva por muito tempo, mesmo que ocorra.dealloc
vez derelease
.Se você estiver escrevendo um objeto de servidor, use argumentos de bloco apenas para conclusão. Não aceite argumentos de bloco para retornos de chamada, como
setEventHandlerBlock:
. Em vez disso, volte ao padrão clássico de delegação: crie um protocolo formal e anuncie umsetEventDelegate:
método. Não retenha o delegado. Se você nem deseja criar um protocolo formal, aceite um seletor como retorno de chamada delegado.E, por último, esse padrão deve tocar alarmes:
Se você está tentando desengatar os blocos que podem se referir
self
de dentrodealloc
, já está com problemas.dealloc
nunca pode ser chamado devido ao ciclo de retenção causado pelas referências no bloco, o que significa que seu objeto simplesmente vazará até que o objeto do servidor seja desalocado.fonte
__weak
adequadamente.__block __unsafe_unretained
modificadores sugeridos na postagem de Kevin podem causar a exceção de acesso incorreto no caso de bloco executado em um thread diferente. É melhor usar apenas o modificador __block para a variável temp e torná-lo nulo após o uso.fonte
Você pode usar a biblioteca libextobjc. É bastante popular, é usado no ReactiveCocoa, por exemplo. https://github.com/jspahrsummers/libextobjc
Ele fornece 2 macros @weakify e @strongify, para que você possa ter:
Isso evita uma referência direta forte, para que não entremos em um ciclo de retenção para si próprio. E também evita que o eu se torne nulo no meio do caminho, mas ainda diminui adequadamente a contagem de retenção. Mais neste link: http://aceontech.com/objc/ios/2014/01/10/weakify-a-more-elegant-solution-to-weakself.html
fonte
Que tal agora?
Não recebo mais o aviso do compilador.
fonte
Bloco: um ciclo de retenção ocorrerá porque contém um bloco que é referenciado no bloco; Se você copiar o bloco e usar uma variável de membro, o self será retido.
fonte