Capturar-se fortemente neste bloco é susceptível de conduzir a um ciclo de retenção

Como posso evitar esse aviso no xcode. Aqui está o trecho 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 }]; 

A captura de self aqui está chegando com o seu access implícito de propriedade de self.timerDisp – você não pode se referir a self ou a propriedades de self dentro de um bloco que será fortemente retido por self .

Você pode contornar isso criando uma referência fraca para self antes de acessar timerDisp dentro do seu bloco:

 __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]; } }; 

E uma coisa muito importante para lembrar: não use variables ​​de instância diretamente no bloco, use-o como propriedades do object fraco, amostra:

 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 } }; 

e não esqueça de fazer:

 - (void)dealloc { self.loadingCompletionHandler = NULL; } 

outro problema pode aparecer se você passar uma cópia fraca de não retida por qualquer object:

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

Se vcToGo for desalocado e então este bloco for triggersdo, acredito que você irá travar com o seletor não reconhecido em uma lixeira que contém a variável vcToGo_ agora. Tente controlá-lo.

Melhor versão

 __strong typeof(self) strongSelf = weakSelf; 

Crie uma referência forte para essa versão fraca como a primeira linha do seu bloco. Se o self ainda existir quando o bloco começar a ser executado e não tiver caído até zero, essa linha garante que ele persista durante toda a vida útil do bloco.

Então a coisa toda seria assim:

 // 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 } }]; 

Eu li este artigo muitas vezes. Este é um excelente artigo de Erica Sadun sobre como evitar problemas ao usar blocos e NSNotificationCenter


Atualização rápida:

Por exemplo, no swift, um método simples com bloco de sucesso seria:

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

Quando chamamos esse método e precisamos nos self utilizar no bloco de sucesso. Nós estaremos usando os resources [weak self] e guard let .

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

Essa chamada dança forte-fraca é usada pelo popular projeto de código aberto Alamofire .

Para mais informações, confira swift-style-guide

Em outra resposta, Tim disse:

você não pode se referir a si mesmo ou a propriedades de si mesmo dentro de um bloco que será fortemente retido por si mesmo.

Isso não é bem verdade. Tudo bem se você fizer isso, desde que você quebre o ciclo em algum momento. Por exemplo, digamos que você tenha um timer que dispare que tenha um bloco que se mantém e que você também mantenha uma forte referência ao timer em si. Isso é perfeitamente correto se você sempre souber que irá destruir o timer em algum momento e interromper o ciclo.

No meu caso, só agora, eu tive esse aviso de código que fez:

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

Agora eu sei que o clang só produzirá esse aviso se detectar que o método começa com “set” (e outro caso especial que não mencionarei aqui). Para mim, eu sei que não há perigo de haver um loop de retenção, então eu mudei o nome do método para “useY:” Claro, isso pode não ser apropriado em todos os casos e geralmente você vai querer usar uma referência fraca, mas Eu achei que valeria a pena notar a minha solução no caso de ajudar os outros.

Adicionando dois centavos para melhorar a precisão e estilo. Na maioria dos casos, você usará apenas um ou dois membros do self neste bloco, provavelmente apenas para atualizar um controle deslizante. Casting self é um exagero. Em vez disso, é melhor ser explícito e lançar apenas os objects de que você realmente precisa dentro do bloco. Por exemplo, se for uma instância do UISlider* , por exemplo, _timeSlider , faça o seguinte antes da declaração do bloco:

 UISlider* __weak slider = _timeSlider; 

Em seguida, basta usar o slider dentro do bloco. Tecnicamente, isso é mais preciso, pois restringe o ciclo de retenção potencial apenas ao object que você precisa, não a todos os objects internos.

Exemplo completo:

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

Além disso, muito provavelmente, o object que está sendo lançado para um ponteiro fraco já é um ponteiro fraco dentro de self mesmo, minimizando ou eliminando completamente a probabilidade de um ciclo de retenção. No exemplo acima, _timeSlider é, na verdade, uma propriedade armazenada como uma referência fraca, por exemplo:

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

Em termos de estilo de codificação, como em C e C ++, as declarações de variables ​​são melhor lidas da direita para a esquerda. Declarar a SomeType* __weak variable nessa ordem é mais natural da direita para a esquerda como: variable is a weak pointer to SomeType .

Intereting Posts