Como definir um ponto de interrupção condicional no Xcode com base em uma propriedade de string de objeto?

90

Estou tentando interromper o depurador quando atingir uma correspondência de string específica. Como exemplo, posso ter algo assim:

Foo myObj = [self gimmeObj];

myObjpode ter uma propriedade chamada name. Quero que o depurador pare na tarefa quando

[myObj.name isEqualToString:@"Bar"];

Como posso definir meu ponto de interrupção condicional no Xcode para fazer isso?

Coocoo4Cocoa
fonte

Respostas:

184

Você pode definir um ponto de interrupção condicional no Xcode definindo o ponto de interrupção normalmente, então clique com o botão direito do mouse nele e selecione Editar Ponto de Interrupção (escolha Executar -> Mostrar -> Pontos de Interrupção).

Na entrada do ponto de interrupção, há uma coluna Condição.

Agora, há vários problemas a serem considerados para essa condição. Em primeiro lugar, o gdb não entende a sintaxe de ponto, portanto, em vez de myObj.name, você deve usar [nome do myObj] (a menos que nome seja um ivar).

A seguir, como com a maioria das expressões em gdb, você deve informar o tipo de resultado de retorno, a saber "BOOL". Portanto, defina uma condição como:

(BOOL)[[myObj name] isEqualToString:@"Bar"]

Freqüentemente, é realmente mais fácil apenas fazer isso no código adicionando temporariamente o código como:

if ( [myObj.name isEqualToString:@"Bar"] ) {
    NSLog( @"here" );
}

e, em seguida, definir o ponto de interrupção no NSLog. Então, sua condição pode ser arbitrariamente complexa sem ter que se preocupar com o que o gdb pode ou não analisar.

Peter N Lewis
fonte
11
Exceto que, ao alterar seu código, você corre o risco de se esquecer de remover o registro ou de alterar o comportamento
Pål Brattberg
3
Isso é verdade. Costumo atenuar isso adicionando "JNI" (ainda não implementado) à sequência, e então minha pesquisa de verificação de pré-lançamento da JNI vai pegá-la.
Peter N Lewis
17
Para fazer isso funcionar eu tive que fazer (bool) maiúsculo como (BOOL), provavelmente uma coisa LLDB.
Wex
1
bool não funcionou para mim no GDB, tive que usar BOOL ou int– a diferença é explicada aqui stackoverflow.com/a/544250/725871 .
Chaosphere2112
2
Você não pode colocá-lo no código se tiver um bug de jogo a cada 200 que finalmente apareceu, e agora você precisa fazer um ponto de interrupção condicional. Parar o programa para alterar o código não é uma opção.
Almo de
17

Aqui está como você faz usando pontos de interrupção condicionais XCode lldb.

Primeiro, clique duas vezes no ponto de interrupção (ou clique com o botão direito edit breakpoint), você pode ver uma janela pop-up.

insira a descrição da imagem aqui

Aqui está o que essa opção significa:

  1. Condição : o ponto de interrupção só disparará sob esta condição.
  2. Ignorar : a quantidade de vezes que a condição precisa atender antes de disparar o ponto de interrupção
  3. Ação : Ação que é executada após as interrupções do ponto de interrupção.
  4. Opções : continuar automaticamente após avaliar as ações

Aqui está um resumo. Para o exemplo acima na imagem, significa que quando a variável buildingIdfor igual a 13, interrompa aqui. Se eu adicionar o tempo de ignorar a 1, ele ignorará a primeira vez quando buildingIdfor igual a 13 e interromperá na segunda vez que a condição for atendida.

Para ações, quando você pressiona adicionar ações, haverá uma lista de escolha. Normalmente o que eu faço é usar o Debugger Command popara imprimir as variáveis ​​que preciso verificar e acredito que existem maneiras melhores de usar as ações do que eu.

Parece que você tem que recompilar e executar o aplicativo se você alterar as condições em tempo de execução

Nuynait
fonte
Provavelmente porque a questão era sobre parar no ponto de interrupção com base em um valor de string [eu não era o votante negativo]
ZS
1
Obrigado, bastante útil. Esta resposta merece mais votos.
andreskwan de
7

Não tenho certeza se isso vai funcionar, mas você pode tentar definir um ponto de interrupção nessa linha de código, abrir o console do depurador (Cmd + Shift + R) e digitar

condition N (int)[[myObj name] isEqualToString:@"Bar"]

Onde N é substituído pelo número do ponto de interrupção (um inteiro).

Adam Rosenfield
fonte
2

Se você alterar myObj.name usando o setter, poderá adicionar um ponto de interrupção simbólico no -[MyObjClass setName:]console do depurador ou no menu Executar-> Gerenciar pontos de interrupção-> Adicionar ponto de interrupção simbólico no Xcode. Se não (por que não? Você provavelmente não deveria estar modificando a variável de instância diretamente, exceto no inicializador ou desalocador designado), você pode definir um ponto de controle no gdb (use o Debugger Console no Xcode assim que o depurador estiver em execução). Esta página explica como. Não acredito que o Xcode exponha uma IU para definir pontos de controle sem usar o console do depurador.

Barry Wark
fonte
0

Às vezes, ao trabalhar com Frameworks (compilações de depuração) e precisar colocar um ponto de interrupção em determinado arquivo / local que é difícil de navegar ou não é exposto publicamente no framework em desenvolvimento. Uma opção é escrever uma classe auxiliar para acionar pontos de interrupção condicionais e tornar a entrada / saída mais fácil.

- (void)invokeFrameworkMethod {
    ...
    [DebugConditionalBreakPointHelper breakPointCondition:YES comment:@"from invokeFrameworkMethod."];
    ...
}

Declaração de cabeçalho na estrutura em desenvolvimento.

#import <Foundation/Foundation.h>

@interface DebugConditionalBreakPointHelper : NSObject
+ (void)breakPointCondition:(BOOL)enabled comment:(NSString *)comment;
@end

E arquivo de implementação:

#import "DebugConditionalBreakPointHelper.h"

@implementation DebugConditionalBreakPointHelper
+ (void)breakPointCondition:(BOOL)enabled comment:(NSString *)comment {
    if (enabled)
    {
        NSLog(@"Triggerred Conditional Break Point. Comment: %@");
    }
}
@end
lal
fonte