Capturarse fuertemente en este bloque probablemente conduzca a un ciclo de retención.
¿Cómo puedo evitar esta advertencia en xcode? Aquí está el fragmento de código:
[player(AVPlayer object) addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil usingBlock:^(CMTime time) {
current+=1;
if(current==60)
{
min+=(current/60);
current = 0;
}
[timerDisp(UILabel) setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];///warning occurs in this line
}];
La captura de self
aquí viene con su acceso implícito a la propiedad de self.timerDisp
: no puede hacer referencia a self
propiedades self
desde dentro de un bloque que será retenido firmemente por self
.
Puedes solucionar esto creando una referencia débil self
antes de acceder timerDisp
dentro de tu bloque:
__weak typeof(self) weakSelf = self;
[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil
usingBlock:^(CMTime time) {
current+=1;
if(current==60)
{
min+=(current/60);
current = 0;
}
[weakSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
}];
__weak MyClass *self_ = self; // that's enough
self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
if (!error) {
[self_ showAlertWithError:error];
} else {
self_.items = [NSArray arrayWithArray:receivedItems];
[self_.tableView reloadData];
}
};
Y una cosa muy importante para recordar: no use variables de instancia directamente en el bloque, úselas como propiedades de un objeto débil, muestra:
self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
if (!error) {
[self_ showAlertWithError:error];
} else {
self_.items = [NSArray arrayWithArray:receivedItems];
[_tableView reloadData]; // BAD! IT ALSO WILL BRING YOU TO RETAIN LOOP
}
};
y no olvides hacer:
- (void)dealloc {
self.loadingCompletionHandler = NULL;
}
Puede aparecer otro problema si pasa una copia débil de un objeto que nadie retiene:
MyViewController *vcToGo = [[MyViewCOntroller alloc] init];
__weak MyViewController *vcToGo_ = vcToGo;
self.loadingCompletion = ^{
[vcToGo_ doSomePrecessing];
};
Si vcToGo
se cancelará la asignación y luego se activará este bloque, creo que se bloqueará con un selector no reconocido en una papelera que vcToGo_
ahora contiene una variable. Intenta controlarlo.
Mejor versión
__strong typeof(self) strongSelf = weakSelf;
Crea una referencia fuerte a esa versión débil como la primera línea de tu bloque. Si self todavía existe cuando el bloque comienza a ejecutarse y no ha vuelto a cero, esta línea garantiza que persista durante toda la vida de ejecución del bloque.
Entonces todo quedaría así:
// Establish the weak self reference
__weak typeof(self) weakSelf = self;
[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil
usingBlock:^(CMTime time) {
// Establish the strong self reference
__strong typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
} else {
// self doesn't exist
}
}];
He leído este artículo muchas veces. Este es un excelente artículo de Erica Sadun sobre cómo evitar problemas al usar bloques y NSNotificationCenter.
Actualización rápida:
Por ejemplo, en Swift un método simple con bloque de éxito sería:
func doSomeThingWithSuccessBlock(success: () -> ()) {
success()
}
Cuando llamamos a este método y necesitamos usarlo self
en el bloque de éxito. Usaremos las funciones [weak self]
y guard let
.
doSomeThingWithSuccessBlock { [weak self] () -> () in
guard let strongSelf = self else { return }
strongSelf.gridCollectionView.reloadData()
}
Esta llamada danza fuerte-débil es utilizada por un popular proyecto de código abierto Alamofire
.
Para obtener más información, consulta la guía de estilo rápido.
En otra respuesta, Tim dijo:
no puede referirse a uno mismo o a sus propiedades desde dentro de un bloque que será retenido fuertemente por uno mismo.
Esto no es del todo cierto. Está bien que hagas esto siempre que rompas el ciclo en algún momento. Por ejemplo, digamos que tiene un temporizador que se activa y que tiene un bloque que se retiene y también mantiene una fuerte referencia al temporizador en sí mismo. Esto está perfectamente bien si siempre sabes que destruirás el temporizador en algún momento y romperás el ciclo.
En mi caso hace un momento, recibí esta advertencia para el código que hacía:
[x setY:^{ [x doSomething]; }];
Ahora sé que clang sólo producirá esta advertencia si detecta que el método comienza con "set" (y otro caso especial que no mencionaré aquí). Para mí, sé que no hay peligro de que haya un bucle de retención, así que cambié el nombre del método a "useY:". Por supuesto, esto puede no ser apropiado en todos los casos y normalmente querrás usar una referencia débil, pero Pensé que valía la pena señalar mi solución en caso de que ayude a otros.