/ / EXC_BAD_ACCESS em [self.tableView reloadData] - iphone, objetivo-c, ios, uitableview, acesso exc-ruim

EXC_BAD_ACCESS em [self.tableView reloadData] - iphone, objetivo-c, ios, uitableview, acesso exc-ruim

Estou criando um aplicativo simples que faz alguns cálculos. Alguns desses cálculos são bastante complexos, por isso pode demorar um pouco para obter o resultado. Estou usando UITableView para permitir que o usuário insira os valores. Depois de clicar no botão "Calcular", faço uma simples UIView com um UIActivityIndicatorView e coloquei no meio da tela. E então eu chamo o método de cálculo em segundo plano. Algum código:

    self.activityIndicatorView = [[UIView alloc]initWithFrame:CGRectMake(320/2 - 50, (480 - 64)/2 - 50, 100, 100)];
self.activityIndicatorView.layer.cornerRadius = 10.0f;
self.activityIndicatorView.backgroundColor = [UIColor colorWithRed:0. green:0. blue:0. alpha:0.7];
UIActivityIndicatorView *actInd = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
actInd.frame = CGRectMake((100 - actInd.frame.size.width) / 2, (100 - actInd.frame.size.height) / 2, actInd.frame.size.width, actInd.frame.size.height);
[actInd startAnimating];
[self.activityIndicatorView addSubview:actInd];
[self.activityIndicatorView bringSubviewToFront:actInd];
[self.view addSubview:self.activityIndicatorView];
[self.view bringSubviewToFront:self.activityIndicatorView];
[self performSelectorInBackground:@selector(calculateAllThatShizzle) withObject:nil];

Como você vê, é bem simples. Mas se o teclado ainda estiver visível, ele trava (EXC_BAD_ACCESS) quando chega a [self.tableView reloadData] no -(void)calculateAllThatShizzle com o método no log:

2012-03-23 11:06:32.418 MyApp[869:5c07] bool _WebTryThreadLock(bool), 0x6adb270: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
1   WebThreadLock
2   -[UITextRangeImpl isEmpty]
3   -[UITextRange(UITextSelectionAdditions) _isCaret]
4   -[UITextSelectionView setCaretBlinks:]
5   -[UIKeyboardImpl setCaretBlinks:]
6   -[UIKeyboardImpl setDelegate:force:]
7   -[UIKeyboardImpl setDelegate:]
8   -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
9   -[UIResponder _finishResignFirstResponder]
10  -[UIResponder resignFirstResponder]
11  -[UITextField resignFirstResponder]
12  -[UITableView reloadData]
13  -[ChildViewController calculateAllThatShizzle]
14  -[NSThread main]
15  __NSThread__main__
16  _pthread_start
17  thread_start

Portanto, o problema é que ele está tentando fazer algo com o teclado a partir de um thread em segundo plano. Eu tentei percorrer as células e chamar [cell.rightTextField resignFirstResponder], mas não ajuda. Eu também tentei usar isso

-(void)reloadTableViewData {
[self.tableView reloadData];
}
-(void)calculateAllThatShizzle {
//some code omitted - code that uses UIKit
if ([dob timeIntervalSinceDate:calcDate]>0) {
[errorButton setTitle:@"Calculation Date must be more than Date of Birth" forState:UIControlStateNormal];
errorButton.hidden = NO;
[self.activityIndicatorView removeFromSuperview];
self.activityIndicatorView = nil;
return;
}
[self performSelectorOnMainThread:@selector(reloadTableViewData) withObject:nil waitUntilDone:NO];
}

mas isso também não ajuda. Estou usando ELCTextfieldCell

Qualquer ajuda será apreciada.

Obrigado

P.S. Eu verifiquei algumas outras perguntas sobre EXC_BAD_ACCESS em reloadData, mas não cobre o problema que tenho

P.P.S. Também posso executar os cálculos no encadeamento principal, mas a GUI fica sem resposta e o indicador de atividade não aparece

Respostas:

1 para resposta № 1

tente este código

for(int i=0;i<[tblViewData count];i++)
{
if([[(ELCTextfieldCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]] rightTextField] isFirstResponder])
{
[[(ELCTextfieldCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]] rightTextField] resignFirstResponder];
}
}

mude de acordo com você.


2 para resposta № 2

Sugestão:

  • make the tableVer o firstResponder antes você chama calculeAllThatShizzle, ou seja, force o primeiro atendedor atual a renunciar e, assim, ignore o teclado
  • e, em seguida, DEFERIR a chamada para o calcularAllThatShizzle após a conclusão do loop de execução atual, ou seja, garantir que o código para descartar o teclado seja executado em um momento adequado

Então, algo assim:

[[self tableView] becomeFirstReponder]; // will dismiss keyboard
[[self performSelector: @selector(startBackgroundStuff) withObject: nil afterDelay: 0.0];

Em seguida, no seu startBackgroundStuff, você pode inserir:

[self performSelectorInBackground:@selector(calculateAllThatShizzle) withObject:nil];

No método, você definitivamente deseja garantir que o método reloadData seja chamado no thread principal. A maneira como você fez parece OK. Você também pode fazer o seguinte:

dispatch_async(dispatch_get_main_queue(), ^{
[self reloadTableViewData];
});

(Não sei se um é melhor que o outro, apenas o que se segue definitivamente funciona de maneira confiável.)

Comentário complementar

Em generateAllThatShizzle, você está fazendo uma chamada UIKit em segundo plano em algumas condições, ou seja, [self.activityIndicatorView removeFromSuperview]; É provável que isso cause um acidente. As chamadas UIKit precisam estar no segmento principal.

Dentro