Me temo que esta pregunta es bastante básica, pero creo que es relevante para muchos programadores de Objective-C que se están metiendo en bloques.
Lo que he escuchado es que, dado que los bloques capturan variables locales a las que se hace referencia como const
copias, el uso self
dentro de un bloque puede dar como resultado un ciclo de retención, en caso de que ese bloque se copie. Por lo tanto, se supone que debemos usar __block
para obligar al bloque a tratar directamente en self
lugar de copiarlo.
__block typeof(self) bself = self;
[someObject messageWithBlock:^{ [bself doSomething]; }];
en lugar de solo
[someObject messageWithBlock:^{ [self doSomething]; }];
Lo que me gustaría saber es lo siguiente: si esto es cierto, ¿hay alguna manera de evitar la fealdad (aparte de usar GC)?
objective-c
memory-management
objective-c-blocks
Jonathan Sterling
fuente
fuente
self
representantesthis
solo para cambiar las cosas. En JavaScript llamo a misthis
cierresself
, por lo que se siente agradable y equilibrado. :)Respuestas:
Estrictamente hablando, el hecho de que sea una copia constante no tiene nada que ver con este problema. Los bloques retendrán cualquier valor obj-c que se capture cuando se creen. Sucede que la solución para el problema de copia constante es idéntica a la solución para el problema de retención; a saber, usar la
__block
clase de almacenamiento para la variable.En cualquier caso, para responder a su pregunta, no hay una alternativa real aquí. Si está diseñando su propia API basada en bloques, y tiene sentido hacerlo, puede hacer que el bloque pase el valor de
self
in como argumento. Desafortunadamente, esto no tiene sentido para la mayoría de las API.Tenga en cuenta que hacer referencia a un ivar tiene exactamente el mismo problema. Si necesita hacer referencia a un ivar en su bloque, use una propiedad en su lugar o use
bself->ivar
.Anexo: Al compilar como ARC,
__block
ya no interrumpe los ciclos de retención. Si está compilando para ARC, debe usar__weak
o en su__unsafe_unretained
lugar.fuente
__weak
está bien. Si sabe con certeza que el objeto no puede estar fuera del alcance cuando se invoca el bloque, entonces__unsafe_unretained
es un poco más rápido, pero en general eso no hará la diferencia. Si lo usa__weak
, asegúrese de incluirlo en una__strong
variable local y probarlonil
antes de hacer algo con él.__block
El efecto secundario de no retener y liberar se debió únicamente a la incapacidad de razonar adecuadamente sobre eso. Con ARC, el compilador ganó esa habilidad, y__block
ahora retiene y lanza. Si necesita evitar eso, debe usarlo__unsafe_unretained
, lo que le indica al compilador que no realice ninguna retención o liberación en el valor de la variable.Solo usa:
Para más información: WWDC 2011 - Bloques y Grand Central Dispatch en la práctica .
https://developer.apple.com/videos/wwdc/2011/?id=308
Nota: si eso no funciona, puedes probar
fuente
Esto puede ser obvio, pero solo tiene que hacer el
self
alias feo cuando sabe que obtendrá un ciclo de retención. Si el bloqueo es solo de una sola vez, creo que puede ignorar con seguridad la retenciónself
. El mal caso es cuando tiene el bloque como interfaz de devolución de llamada, por ejemplo. Como aquí:Aquí la API no tiene mucho sentido, pero tendría sentido cuando se comunica con una superclase, por ejemplo. Retenemos el controlador de búfer, el controlador de búfer nos retiene. Compare con algo como esto:
En estas situaciones no hago el
self
aliasing. Obtendrá un ciclo de retención, pero la operación es de corta duración y el bloque eventualmente se quedará sin memoria, rompiendo el ciclo. Pero mi experiencia con los bloques es muy pequeña y podría ser que elself
alias salga como una mejor práctica a largo plazo.fuente
copy
, noretain
. Si son justosretain
, entonces no hay garantía de que se moverán de la pila, lo que significa que cuando vayas a ejecutarlo, ya no estará allí. (y la copia y el bloque ya copiado está optimizado para retener)retain
fase hace un tiempo y rápidamente me di cuenta de lo que estás diciendo :) ¡Gracias!retain
se ignora por completo para los bloques (a menos que ya se hayan movido de la pila concopy
).Publicar otra respuesta porque esto también fue un problema para mí. Originalmente pensé que tenía que usar blockSelf en cualquier lugar donde hubiera una autorreferencia dentro de un bloque. Este no es el caso, es solo cuando el objeto en sí tiene un bloque. Y, de hecho, si usa blockSelf en estos casos, el objeto puede ser desasignado antes de recuperar el resultado del bloque y luego se bloqueará cuando intente llamarlo, por lo que claramente desea que se retenga hasta la respuesta Vuelve.
El primer caso demuestra cuándo ocurrirá un ciclo de retención porque contiene un bloque al que se hace referencia en el bloque:
No necesita blockSelf en el segundo caso porque el objeto que realiza la llamada no tiene un bloque que causará un ciclo de retención cuando haga referencia a self:
fuente
self
pueden no deberse a que las personas apliquen en exceso esta solución. Este es un buen ejemplo de cómo evitar los ciclos de retención en el código que no es ARC, gracias por publicar.Recuerde también que los ciclos de retención pueden ocurrir si su bloqueo se refiere a otro objeto que luego retiene
self
.No estoy seguro de que la recolección de basura pueda ayudar en estos ciclos de retención. Si el objeto que retiene el bloque (que llamaré el objeto del servidor) sobrevive
self
(el objeto del cliente), la referencia aself
interior del bloque no se considerará cíclica hasta que se libere el objeto de retención. Si el objeto del servidor supera a sus clientes, es posible que tenga una pérdida de memoria significativa.Como no hay soluciones limpias, recomendaría las siguientes soluciones. Siéntase libre de elegir uno o más de ellos para solucionar su problema.
doSomethingAndWhenDoneExecuteThisBlock:
, y no métodos comosetNotificationHandlerBlock:
. Los bloques utilizados para la finalización tienen un final definitivo de vida útil, y los objetos del servidor deben liberarlos después de evaluarlos. Esto evita que el ciclo de retención viva demasiado tiempo, incluso si ocurre.dealloc
lugar derelease
.Si está escribiendo un objeto de servidor, tome argumentos de bloque solo para completar. No acepte argumentos de bloque para devoluciones de llamada, como
setEventHandlerBlock:
. En cambio, recurra al patrón de delegado clásico: cree un protocolo formal y anuncie unsetEventDelegate:
método. No retener al delegado. Si ni siquiera desea crear un protocolo formal, acepte un selector como devolución de llamada delegada.Y, por último, este patrón debería hacer sonar las alarmas:
Si está tratando de desenganchar bloques que pueden referirse
self
desde adentrodealloc
, ya está en problemas.dealloc
Es posible que nunca se llame debido al ciclo de retención causado por las referencias en el bloque, lo que significa que su objeto simplemente se filtrará hasta que el objeto del servidor se desasigne.fuente
__weak
adecuadamente.__block __unsafe_unretained
los modificadores sugeridos en la publicación de Kevin pueden causar una excepción de acceso incorrecto en caso de que el bloque se ejecute en un hilo diferente. Es mejor usar solo el modificador __block para la variable temporal y hacerlo nulo después del uso.fuente
Puede usar la biblioteca libextobjc. Es bastante popular, se usa en ReactiveCocoa, por ejemplo. https://github.com/jspahrsummers/libextobjc
Proporciona 2 macros @weakify y @strongify, por lo que puede tener:
Esto evita una referencia directa fuerte para que no entremos en un ciclo de retención para uno mismo. Y también, evita que el yo se vuelva nulo a mitad de camino, pero aún así disminuye adecuadamente el conteo de retención. Más en este enlace: http://aceontech.com/objc/ios/2014/01/10/weakify-a-more-elegant-solution-to-weakself.html
fuente
¿Qué tal esto?
Ya no recibo la advertencia del compilador.
fuente
Bloque: se producirá un ciclo de retención porque contiene un bloque al que se hace referencia en el bloque; Si realiza la copia en bloque y utiliza una variable miembro, self retendrá.
fuente