Pregunta capturar a uno mismo con fuerza en este bloque es probable que 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
}];

200
2018-01-28 06:30


origen


Respuestas:


La captura de self aquí está entrando con su acceso de propiedad implícita de self.timerDisp - no puedes referirte a self o propiedades en self desde dentro de un bloque que será fuertemente retenido por self.

Puede evitar esto creando una referencia débil a 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]];
                                            }];

491
2018-01-28 06:39



__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, úselo como propiedades del 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 va a pasar una copia débil no retenida por cualquier objeto:

MyViewController *vcToGo = [[MyViewCOntroller alloc] init];
__weak MyViewController *vcToGo_ = vcToGo;
self.loadingCompletion = ^{
    [vcToGo_ doSomePrecessing];
};

Si vcToGo será desasignado y luego se disparará este bloque, creo que se bloqueará con un selector no reconocido en una papelera que contiene vcToGo_ variable ahora. Intenta controlarlo


51
2018-02-18 16:22



Mejor versión

__strong typeof(self) strongSelf = weakSelf;

Crea una referencia fuerte a esa versión débil como la primera línea en tu bloque. Si aún existe uno cuando el bloque comienza a ejecutarse y no ha vuelto a cero, esta línea asegura que persista a lo largo de la vida útil de ejecución del bloque.

Entonces todo serí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 en Cómo evitar problemas al usar bloques y NSNotificationCenter


Swift actualización:

Por ejemplo, en forma rápida un método simple con bloque de éxito sería:

func doSomeThingWithSuccessBlock(success: () -> ()) {
    success()
}

Cuando llamamos a este método y necesitamos usar self en el bloque de éxito Usaremos el [weak self] y guard let caracteristicas.

    doSomeThingWithSuccessBlock { [weak self] () -> () in
        guard let strongSelf = self else { return }
        strongSelf.gridCollectionView.reloadData()
    }

Este llamado baile fuerte-débil es utilizado por el popular proyecto de código abierto Alamofire.

Para más información echa un vistazo guía de estilo rápido


36
2018-05-13 10:40



En otra respuesta, Tim dijo:

no puede referirse a uno mismo o a las propiedades de uno mismo desde dentro de un bloque que será fuertemente retenido 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, supongamos que tienes un temporizador que dispara que tiene un bloque que se conserva y también guardas 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 justo ahora, tuve esta advertencia para el código que hizo:

[x setY:^{ [x doSomething]; }];

Ahora sé que el clang solo 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, eso podría no ser apropiado en todos los casos y generalmente querrás usar una referencia débil, pero Pensé que valía la pena señalar mi solución en caso de que ayudara a otros.


14
2018-03-25 00:51



Agregar dos centavos para mejorar la precisión y el estilo. En la mayoría de los casos, solo utilizará uno o un par de miembros de self en este bloque, lo más probable es que simplemente actualice un control deslizante. Fundición selfes exagerado En cambio, es mejor ser explícito y emitir solamente los objetos que realmente necesitas dentro del bloque. Por ejemplo, si se trata de una instancia de UISlider*, por ejemplo, _timeSlider, haz lo siguiente antes de la declaración de bloque:

UISlider* __weak slider = _timeSlider;

Entonces solo usa slider dentro del bloque. Técnicamente, esto es más preciso ya que reduce el ciclo de retención de potencial a solo el objeto que necesita, no todos los objetos dentro self.

Ejemplo completo:

UISlider* __weak slider = _timeSlider;
[_embeddedPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
     queue:nil
     usingBlock:^(CMTime time){
        slider.value = time.value/time.timescale;
     }
];

Además, lo más probable es que el objeto arrojado a un puntero débil ya sea un puntero débil dentro self así como minimizar o eliminar por completo la probabilidad de un ciclo de retención. En el ejemplo anterior, _timeSlider es en realidad una propiedad almacenada como una referencia débil, por ejemplo:

@property (nonatomic, weak) IBOutlet UISlider* timeSlider;

En términos de estilo de codificación, como en C y C ++, las declaraciones de variables se leen mejor de derecha a izquierda. Declarando SomeType* __weak variable en este orden se lee más naturalmente de derecha a izquierda como: variable is a weak pointer to SomeType.


0
2017-12-22 18:54