09 Fev, 2011 18:00
Dicas para ajudar a depurar o código em Objective-C
Nesse post recolhemos algumas dicas úteis que utilizamos em nossos projetos. Vale lembrar que a maioria delas já está disponível na internet, o que fizemos foi juntá-los aqui.
Melhorias no Log
Para aqueles que usam e abusam do NSLog, devem sempre ter em mente que ele sai no console do aparelho. Ou seja, vai que você esquece de apagar aquele NSLog que escreve algo que o usuário não deveria saber. Aí, basta que o usuário tenha o Xcode ou o iPhone Configuration Utility, e - que beleza! - está lá a informação que voce não deveria mostrar.
Outro ponto que alguns desenvolvedores reclamam é de ter sempre que usar texto formatado no NSLog. Os que vieram de Java provavelmente sentem saudades do System.out.println(), que, embora verborrágico, ao menos permitia colocar lá o objeto que quisesse, e funcionava.
Por conta disso, criamos algumas diretivas no Prefix.pch para facilitar nossa vida. Veja o código a seguir:
#ifdef DEBUG
#define myLog(format, ...) NSLog(@"%s:%@", __PRETTY_FUNCTION__,[NSString stringWithFormat:format, ## __VA_ARGS__]);
#define myLogD(obj) NSLog(@"%s:%@", __PRETTY_FUNCTION__,[NSString stringWithFormat:@"%@", obj]);
#define myLogI(myint) NSLog(@"%s:%@", __PRETTY_FUNCTION__,[NSString stringWithFormat:@"%d", myint]);
#define myLogF(myfloat) NSLog(@"%s:%@", __PRETTY_FUNCTION__,[NSString stringWithFormat:@"%f", myfloat]);
#define MARK myLog(@"");
#else
#define myLog(format,...)
#define myLogD(obj)
#define myLogI(myint)
#define myLogF(myfloat)
#define MARK
#endif
A gente criou o myLog(), que substitui o NSLog do Objective-C. Perceba que o #ifdef inicial distingue o comportamento do método para distribuição dos outros modos. Em distribuição, o myLog() não faz nada. Já nas outras configurações ele joga no output o resultado do método NSLog. Isso só funciona porque ao criar um projeto o Xcode já cria uma variável chamada DEBUG:
Percebeu que ele tem um __PRETTY_FUNCTION__ no método? Pois é, isso serve para imprimir também o nome do método de onde o myLog() foi chamado. Mais uma facilidade que ajuda muito na hora de ver o valor no output.
Depois disso, temos mais três métodos:
#define myLogD(obj) NSLog(@"%s:%@", __PRETTY_FUNCTION__,[NSString stringWithFormat:@"%@", obj]);
#define myLogI(myint) NSLog(@"%s:%@", __PRETTY_FUNCTION__,[NSString stringWithFormat:@"%d", myint]);
#define myLogF(myfloat) NSLog(@"%s:%@", __PRETTY_FUNCTION__,[NSString stringWithFormat:@"%f", myfloat]);
O primeiro, o myLogD(), serve para exibir a descrição de um objeto. Digamos que você tenha um objeto cliente. Basta utilizar o método dessa forma:
myLogD(cliente);
e ele imprime na tela a descrição dele. O segundo e o terceiro servem para variáveis inteiras e de ponto flutuante.
E tem o último método, o MARK. Lembra das vezes em que você teve que depurar, e queria saber se um método era chamado na execução do programa? Em tempos antigos você utilizaria NSLog(@"passei no método conectar()"), ou algo semelhante. Pois bem, com o MARK, basta escrever MARK no código, e ele imprime o nome do método na saída. Simples, não?
O temido EXC_BAD_ACCESS
De todos os erros genéricos que o Objective-C pode te mostrar, um dos piores é o EXC_BAD_ACCESS. O EXC_BAD_ACCESS é lançado quando o programa tenta enviar uma mensagem (por exemplo, a chamada de um método) para um objeto que já foi liberado (released). Na maioria das vezes, quando o erro é detectado, a pilha de processos onde o objeto estava já se foi, o que dificulta descobrir onde o erro realmente aconteceu.
Mas há uma solução que na maioria dos casos consegue mapear qual objeto foi liberado: basta utilizar a variável de ambiente NSZombieEnabled. Com ela, o Objective-C deixa alocado na memória um objeto dummy, guardando a referência. Quando acontece o EXC_BAD_ACCESS, ele retorna o objeto e a mensagem que foi enviada para ele.
Para habilitar essa variável é bem simples: na seção 'Executables', clique duas vezes no executável do seu projeto. Na aba 'Arguments', na seção 'Variables to be set in the enviroment', adicione a variável NSZombieEnabled, dando o valor YES para ela. Para habilitar, basta marcar o check a esquerda.
Vale chamar a atenção para um detalhe: como o NSZombieEnabled é um recurso que guarda na memória os objetos que foram liberados, tenha em mente que ela é um auxílio para testes, e, portanto, não deixe essa variável habilitada. Aconteceu o EXC_BAD_ACCESS, utilize o NSZombieEnabled, descubra onde foi o problema e desabilite, para não correr o risco de seu programa utilizar mais memória do que ele realmente necessita.
Milhares de Breakpoint
Imagine o cenário: você testou o que queria testar, utilizou centenas de breakpoints. Agora quer testar outra coisa, e quer tirar todos os breakpoints que não servem mais? Algumas pessoas não percebem, mas o Xcode permite que você os retire de maneira mais fácil. Na seção 'Groups and files', a esquerda, um dos últimos itens é o de Breakpoints. Lá você pode retirar todos os breakpoints de uma vez, basta selecionar e excluir.
Enfim, por hora é só. Caso tenha alguma dúvida, ou sugestão, é só colocar nos comentários. Abraço a todos.