Depuração avançada

Área de depuração é um recurso útil no trabalho de desenvolvedores do iOS no Xcode. Assim que começamos a dominar o desenvolvimento do iOS e tentamos nos afastar do método de impressão familiar e amado, e encontramos métodos mais rápidos e convenientes para entender o estado do sistema em um determinado período, começamos a estudar a Área de Depuração.

Provavelmente, no painel de depuração, seus olhos caem antes que você entenda o que exatamente acontece lá. Quando o aplicativo falha pela primeira vez, o menu inferior é aberto automaticamente, inicialmente pode ajudar a entender o problema (lembre-se do bom e velho “Erro fatal: Índice fora do intervalo”), basicamente no começo, você não entende o que o Xcode quer de nós e o faz no Google. erros, mas no decorrer do crescimento, mais e mais informações se tornarão claras.

Desde o início, o programador está tentando otimizar seu trabalho. Para fazer isso, nos esforçamos para entender em que ponto nosso programa entrou em um estado incorreto. E aqui, dependendo do ponto em que a evolução do programador está localizada, os métodos podem variar. Primeiro, como a depuração é feita corretamente pelo método “print ()”, os pontos de interrupção são colocados e os métodos “po” são chamados, depois a familiarização com a entrada variável de depuração (a área ao lado do console no Xcode) e, em seguida, vem o entendimento de como compilar o código ao parar Métodos de ponto de interrupção - "expressão" (pelo menos essa foi a evolução que tive).

Vamos tentar maneiras diferentes que nos ajudarão a entender e alterar o estado de nosso aplicativo. Os mais simples como "print ()" e "po" não serão considerados, acho que você já entende a essência deles e sabe como aplicá-la.

Vamos criar um aplicativo simples com uma tela na qual teremos apenas um tipo de célula (TableViewcell) com dois elementos internos: UIImageView e UILabel. Nas células, escreveremos seu número de série e colocaremos imagem1 ou imagem2 na imagem.

O método tableViewCellForRowAtIndexPath criará células para nós, colocará os dados e retornará:

imagem

Este método irá gerar a seguinte tabela:

imagem

Ponto de interrupção


Vamos parar o nosso programa e adicionar algum texto ao nosso Label.

1. Defina o ponto de interrupção:

imagem

2. O programa interrompeu a execução na linha 55, imediatamente após a atribuição do texto. Como estamos em uma linha localizada no campo de visão da célula, podemos interagir com nossa célula.

3. Escrevemos no console um comando para alterar o texto da célula:

imagem

4. Removemos nosso ponto de interrupção e pressionamos o botão "continuar a execução do programa".

5. Na tela do nosso telefone, vemos que tudo saiu com sucesso:

imagem

expressão executa a expressão e retorna um valor no encadeamento atual.

Ponto de interrupção editado


Mas, e se precisarmos alterar o texto em um grande número de células? Ou já percebemos no processo de execução do programa que precisamos mudar?

Podemos otimizar a execução desta operação e acelerar um pouco o trabalho, alterar o texto para as células quando atingir o Breakpoint e continuar a executar o programa, isso economizará muito tempo e nos permitirá não imprimir a mesma coisa para cada célula.

Para fazer isso, precisamos modificar levemente nosso ponto de interrupção, adicionar um código adicional lá, que mudará seu texto na zona de visibilidade da nossa célula e continuará o programa.

  1. Crie um ponto de interrupção.
  2. Clique com o botão esquerdo na seta do ponto de interrupção.
  3. Clique em Editar ponto de interrupção.
  4. Condição - condições sob as quais o Breakpoint funcionará, agora não precisamos dele.
  5. Ignorar - quantas vezes pular o Breakpoint antes que ele funcione (também não é isso).
  6. Mas ação é o que precisamos, selecione o tipo de ação Comando Debugger.
  7. Escrevemos a expressão que precisamos executar:
  8. expressão cell.desriptionTextOutlet.text = "\ (indexPath.item) missão concluída".
  9. Colocar um sinal - Continue a execução após a conclusão bem-sucedida do comando.

imagem

9. Estamos tentando.

imagem

Foi um sucesso, acabou por alterar o texto de cada célula durante a formação da tabela, e não tivemos que sacrificar o tempo e prescrever operações para cada uma.

Função Breakpoint


Há sempre momentos em que algo acontece em nosso aplicativo que não podemos explicar, o texto não muda ou muda mais do que o necessário, parece que o Breakpoint, nesse caso, não tem para onde colocar. Mas isso não é totalmente verdade, se você conhece o Obj-C e sabe qual método o compilador executa que deseja rastrear, você pode colocar o Breakpoint nele e na próxima vez que o método for chamado, o aplicativo será interrompido no processo de execução do código do Assembler.

1. No navegador Breakpoint, selecione Symbolical Breakpoint.

imagem

2. Queremos acompanhar o método de configuração do texto na célula, escreva - [UILabel setText:].

imagem

3. O argumento zero não existe e a contagem começa a partir do primeiro. O primeiro método capturado não é o que precisamos (definimos o horário atual para a barra de status), mas o segundo é apenas o nosso:

4. Em "$ arg1", a descrição do objeto é armazenada.

5. Em "$ arg2", a função seletora é armazenada.

6. Em "$ arg3", o texto obtido pelo método é armazenado.

Ok, isso parece estar claro. Mas às vezes há situações em que não se limita a definir um texto na barra de status e você precisa acompanhar a execução do método em um controlador específico, o que deve ser feito? Você pode ativar o ponto de interrupção semelhante ao que instalamos anteriormente, mas definindo sua posição no código. O que isso significa? Sabemos com certeza que nossa visualização aparecerá quando instalarmos o texto na célula, portanto, é preciso colocá-lo em viewDidLoad ou após a criação da célula.

Para criar um ponto de interrupção, definimos na linha e, na ação, escrevemos o seguinte código:

breakpoint set --one-shot true --name "-[UILabel setText:]” 

breakpoint set —one-shot true instantâneo - criar ponto de interrupção
—name é o nome do ponto de interrupção do caractere
“-[UILabel setText:]” chamado método

Aqui está o que aconteceu:

imagem

Ignorar linhas



Mas e se suspeitarmos que alguma linha de código estraga todo o programa? Durante a execução do código, você pode evitar a execução de uma linha de código específica como esta:

  1. Colocamos o ponto de interrupção em uma linha que não gostaríamos de executar.
  2. Quando a execução parar, arraste-o para a linha com a qual queremos continuar a execução do programa (engraçado, mas nem sempre funciona, a opção sem arrastar é mais baixa).

Há também outra opção que otimizará o salto de linha - esta é a prescrição do comando apropriado no "editar ponto de interrupção". A equipe é arriscada, já que a essência de tais saltos é nos salvar da reconstrução, mas se você pular a inicialização do objeto e tentar contatá-lo, o programa falhará.

imagem

Vamos parar nosso programa de inicializar a figura e não atribuiremos a figura à célula. Para isso, precisamos pular cinco linhas de código e retornar a célula sem uma figura. Para isso, pular a execução das cinco linhas de código a seguir no segmento atual e continuar o programa:

imagem

Parece muito bom, mas ainda quero atribuir uma imagem, vamos adicionar um método de atribuição ao ponto de interrupção:

imagem

Uma boa combinação, agora temos apenas um tipo de imagem em cada célula.

Watchpoint


Outra função conveniente no depurador é rastrear valores no programa, pontos de controle. O Watchpoint é um pouco semelhante ao KVO, definimos um ponto de interrupção para alterar o estado de um objeto e, cada vez que ele muda de estado, o processo de execução do programa para e podemos ver o valor e os locais de onde e por quem o valor foi alterado. Por exemplo, coloquei um ponto de controle em uma célula para descobrir o que acontece quando uma tabela é paginada e uma nova célula é inicializada. A lista de comandos acabou sendo muito grande, por isso não vou mencionar apenas alguns: execução da exibição de layout localizada dentro da célula e restrição de configuração, animação, estados de configuração da célula e muito, muito mais.

Para definir o ponto de controle como um valor, você deve interromper o programa de ponto de interrupção no escopo das propriedades que deseja monitorar, selecionar a propriedade no painel "variável de depuração" e selecionar assistir "<parâmetro>".

imagem

Para remover o ponto de controle com uma variável, é necessário olhar para o navegador do ponto de interrupção, pois junto com o restante do ponto de interrupção será o nosso ponto de controle.

Alteração na interface do ponto de interrupção


Às vezes, precisamos aprender mais sobre o objeto que estamos tentando repelir. A opção mais fácil é usar "po" para exibir informações sobre o objeto e verificar a localização do objeto na memória no mesmo local. Mas acontece que não temos um link direto para um objeto; ele não é representado na exibição da API, na qual reside ou está possivelmente oculto pela biblioteca. Uma das opções é usar a hierarquia de visualizações, mas isso nem sempre é conveniente e nem sempre é difícil entender que você encontrou a visualização desejada. Você pode tentar usar o comando:

 expression self.view.recursiveDescription() 

Está no Obj-C, mas foi removido no Swift devido às peculiaridades do idioma, não podemos fazê-lo, mas como o Debuger trabalha com o Obj-C, em teoria, ele pode alimentar esse comando e ele entenderá o que você deseja dele. . Para executar o código Obj-C no console, você deve inserir o comando:

 expression -l objc -O - - [`self.view` recursiveDescription] 

O que você vê aqui? Vejo uma construção bastante inconveniente com a qual podemos nos acostumar com o tempo, mas é melhor não fazê-lo, mas use tipealias para simplificar o comando:

 command alias poc expression -l objc -O — 

Agora, nossa equipe está diminuindo e simplificando, mas continua fazendo o trabalho:

 poc [`self.view` recursiveDescription] 

Funcionará após o fechamento do Xcode ou em outro projeto? Infelizmente, não. Mas pode ser consertado! Criando o arquivo .lldbinit e inserindo nosso alias lá. Se você não sabe, aqui estão as instruções sobre os itens:

1. Crie um arquivo .lldbinit (você pode usar o .gitignore como um protótipo, ele pertence ao mesmo tipo de arquivos invisíveis de texto).

2. Escreva exatamente o seguinte comando neste arquivo:

  command alias poc expression -l objc -O - - 

3. Coloque o arquivo na pasta pelo endereço “MacintoshHD / Usuários / <Aqui está o nome do seu usuário>”.

E, assim, obtivemos uma descrição de todas as visualizações apresentadas na tela. Vamos tentar ver o que podemos fazer com o endereço dos objetos na memória. Para Swift, existe um método com uma desvantagem, você sempre deve converter o tipo do objeto na memória para um determinado valor:

 po unsafeBitCast(0x105508410, to: UIImageView.self) 

Agora podemos ver a posição da nossa imagem na célula, vamos movê-la para que ela fique centralizada na célula e tenha um recuo ao lado de 20 px.

imagem

Às vezes, uma alteração não é percebida imediatamente, mas é necessário remover o aplicativo da depuração para perceber a alteração.

Mas se queremos ver algo semelhante em cada célula, precisamos acelerar a execução de comandos, podemos escrever vários scripts em Python que funcionarão para nós (você pode ver como adicionar scripts aqui www.raywenderlich.com/612-custom-lldb-commands-in- prática ), e se você souber como lidar com Python e quiser escrever nele para o lldb, será útil.

Decidi escrever uma extensão para a classe UIView, que simplesmente moveria a exibição na direção certa; parecia-me que haveria menos problemas ao conectar novos scripts ao LLDB e não seriam difíceis para qualquer programador iOS (caso contrário, você precisará aprender Python para LLDB).

Eu não procurei o local do objeto na memória e o trouxe para a classe desejada, para que eu capturasse um quadro mais tarde, também levaria muito tempo. A questão foi resolvida escrevendo uma função na extensão UIView:

imagem

Infelizmente, ele não funciona bem com células, provavelmente devido ao fato de que no momento em que o comando flush foi executado, nem todas as posições das células foram calculadas e não apareceram na tela (ainda não retornamos o tableViewCell). Com outros elementos estáticos, funciona bem.

Conhecendo a posição da vista na hierarquia, podemos acessá-la e mudar sua posição.

E agora a situação inversa é quando podemos acessar o ViewHierarchy e desejar obter dados de exibição a partir daí. No Xcode, é possível visualizar a hierarquia de visualizações durante a execução de um programa, também é possível visualizar cores, layout, tipos e ligações para outros objetos, incluindo isso. Vamos tentar acessar as restrições do nosso UIImageView.

Para obter dados de restrição:

1. Clique na hierarquia de exibição de depuração.
2. Ative o Conteúdo cortado no painel na parte inferior da tela que aparece.
3. Ative restrições no mesmo painel.
4. Selecione Contraint.
5. No menu, clique em Editar -> Copiar (Command + C).
6. A ligação deste tipo é copiada: ((NSLayoutConstraint *) 0x2838a39d0).
7. E agora, assim como o alteramos através do código, você também pode alterá-lo no lldb:
expression [((NSLayoutConstraint *)0x2838a39d0) setConstant: 60]
8. Após clicar no botão continuar, o item atualizará sua posição na tela.

Você pode alterar cores, texto e muito mais da mesma maneira:

 expression [(UILabel *)0x102d0a260] setTextColor: UIColor.whiteColor] 

O projeto de demonstração acabou sendo muito simples (60 linhas de código no ViewController), a maior parte do código que escrevi é apresentada no artigo, portanto não haverá dificuldade em reproduzir o projeto de teste.

PS: Se você tiver perguntas ou comentários, escreva. Dê uma olhada no WWDC e Debate como Pro.

Também aconselho que você se familiarize com os materiais:

Inspirado pelo Advanced Debugger WWDC 18 Session
Comandos do depurador
Adicionando scripts Python ao LLDB Xcode

Source: https://habr.com/ru/post/pt460779/


All Articles