
Às vezes, você pode se encontrar em uma situação em que seu aplicativo não pode ter um bom desempenho. Então, aqui estão alguns instrumentos que você pode usar e as práticas recomendadas que você pode implementar para melhorar as coisas.
Esta é a segunda parte do artigo, com base na palestra proferida por Luke Parham, engenheiro de iOS da Fyusion e autor de tutoriais para desenvolvimento de iOS em RayWenderlich.com, na International Mobile Developers Conference
MBLT DEV em 2017.
Instrumento de animação principal
Se você fez muitos perfis e encontrou todos os seus gargalos, às vezes ainda existem problemas de desempenho. Isso ocorre pela maneira como as coisas da interface do usuário funcionam no iOS. Sempre que você define quadros ou faz UIViews, o que realmente acontece é que você faz uma transação CAT ou o sistema faz isso para você. E eles são enviados para uma coisa chamada "o servidor de renderização". O servidor de renderização é responsável por fazer animações. Se você fizer um UIView animateWith: tanto faz, tudo isso acontecerá no servidor de renderização, que é outro encadeamento e ele lida com todas as animações do aplicativo.

Aqui está um Time Profiler que possui um medidor de taxa de quadros na parte superior. E na parte inferior está a parte mais importante das opções de depuração. Vamos contar sobre as duas coisas mais importantes e fáceis de corrigir.

O primeiro são as camadas de cores combinadas. É realmente fácil de corrigir. E isso nos leva à primeira seção da polícia de desempenho. Basicamente, muitos aplicativos têm problemas: até o iMessage, amado aplicativo da Apple, está fazendo muitas coisas não muito boas. Aqui vemos que há muito vermelho:

Vermelho significa que você tem etiquetas com fundo branco. E então eles estão em cima de outro fundo branco e, por algum motivo, não estão definidos para serem opacos. Portanto, o liquidificador está misturando essas cores, branco e branco e, consequentemente, obtendo uma cor branca. Para cada pixel vermelho, ele está fazendo cálculos extras sem nenhum benefício, você ainda fica branco em segundo plano.
Para evitar isso, você pode tornar as camadas opacas sempre que possível, se forem da mesma cor e da mesma cor. Se a subvisão tiver a mesma cor de fundo, a mistura não será necessária. Tudo o que você precisa fazer é definir a opacidade das suas camadas como 1 e, em seguida, verifique se a cor do plano de fundo está definida. Se a cor do fundo for clara, ela nem sempre será opaca.

Renderização fora da tela
Os elementos renderizados fora da tela serão mostrados em amarelo se você ativar esta opção. O bom do instrumento Core Animation é que você pode ver outros aplicativos. Você pode ativar essas opções e, em seguida, acessar qualquer aplicativo em seu sistema e ver o que eles estão fazendo de errado. Nesse caso, o Instagram tem essas pequenas bolhas no topo que mostram as histórias das pessoas.

Como você pode ver, todos são amarelos. No iPhone 5, eles são agressivamente lentos. E isso ocorre porque a renderização fora da tela é muito pior que a mistura alfa. Ele interrompe a GPU. Ele acaba precisando fazer cálculos extras entre a GPU e a CPU, para que você obtenha paradas adicionais desnecessárias na maioria das vezes.
Caminho de Bezier em vez de ver curvas
A próxima regra: não use a propriedade raio do canto. Se você tiver uma visualização e definir view.layer.cornerRadius, isso sempre apresentará a renderização fora da tela. Em vez disso, você pode usar um caminho mais bege e o mesmo tipo de material do CGBitmap anterior. Nesse caso, um contexto UIGraphics. Essa função opera com o UIImage, que possui um tamanho, faz cantos arredondados com base nesse tamanho e usa um caminho de bezier para recortar. Em seguida, recortamos a imagem e a retornamos do contexto UIImage. Portanto, isso retornará uma imagem pré-arredondada em vez de arredondar a visualização em que a imagem fica dentro.

O último exemplo. Aqui está o Twitter e esta é uma visualização em tempo real dessa animação em execução. Ele deve abrir e mostrar as informações, mas todo esse texto e outras informações foram renderizadas fora da tela, tornando a animação lenta. Essa é a coisa com menos desempenho que eu já encontrei em um aplicativo que está na App Store.

Então, como isso aconteceu? Uma coisa que causa isso é a propriedade shouldRasterize de um CALayer. É uma opção em uma camada que permite armazenar em cache as texturas que foram renderizadas. Existem muitas regras estranhas. Por exemplo, se não tiver sido usado em uma certa quantidade de milissegundos, ele deixará o cache. E então, se ele sair do cache, ele será renderizado fora da tela em todos os quadros. Realmente não vale os possíveis benefícios que possui. E é difícil verificar se está realmente beneficiando você.
Sumário
Evite renderização fora da tela e mistura alfa, se puder. Às vezes, a mistura alfa é necessária. É melhor que a renderização fora da tela. A renderização fora da tela ocorre por alguns motivos. Isso pode acontecer das sombras; isso pode acontecer a partir do arredondamento de cantos; isso pode acontecer do mascaramento.
Torne as visualizações opacas quando possível. Não use a propriedade raio do canto; use os caminhos de Bezier o máximo possível. Além disso, não use as propriedades de sombra da camada se estiver fazendo sombras de texto. Você pode usar o NSShadow.
Rastreio de atividade
O rastreamento de atividade é um tipo de versão de nível muito mais baixo de algo que o criador de perfil de tempo faria. Ele fornece uma visão de todos os seus threads e como eles estão interagindo. E é bem complicado. Mas tem recursos muito bons que você pode configurar.
Rastreio do sistema
Use o
Rastreio do sistema para rastrear horários para eventos específicos. Você pode configurar maneiras de rastrear eventos e seções de código específicos e ver quanto tempo eles levam em um aplicativo do mundo real. Ele permite que você obtenha informações detalhadas sobre o que está acontecendo no seu sistema.
- Use "Assinar mensagens" para sinalizar quando algo importante acontece.
- Os pontos são eventos únicos quando / se você deseja ver como uma animação ocorreu ou algo parecido.
- As regiões têm um começo e um fim. Para decodificação de imagens, você pode ver quando é iniciado e quando termina para estimar quanto tempo levou em geral.

Aqui está como você configura um modelo de rastreamento do sistema. Você faz essa lista de eventos que podem acontecer. Então, o número um é um download de imagem. Dois é uma decodificação de imagem e três é essa animação de inclinação que adicionei. Basicamente, você configura algumas opções extras para ver quais serão as cores. Basicamente, você envia um número como 1 ou 2; ele será vermelho ou verde com base no que você enviar lá.

Se você estiver no Objective-C, precisará importar este cabeçalho kdebug_signpost. No Swift, ele está disponível apenas para você.

E então você deve chamar esta função, kdebug_signpost ou kdebug_signpost_start e kdebug_ signpost_end. E eles funcionam com o código que você passou. Então, organizamos esses três eventos com esses números. Então você passa esse número aqui. Você passa um objeto que é basicamente a chave para esse evento. E então, o último número é a cor. Então 2 é como se você soubesse vermelho ou algo assim.
Eu tenho um projeto de exemplo do Swift no
GitHub . Eu meio que simplifiquei as coisas. Há um começo e um fim que são um pouco mais fáceis de lidar.
É assim que parecerá quando você executar um rastreamento. Ele não mostrará nada a princípio. Então, quando você mata o aplicativo, ele faz alguns cálculos e mostra algumas coisas aqui.

Aqui podemos ver nossos downloads de imagens, que levaram cerca de 200 milissegundos. E então, há decodificação de imagem que levou 40 milissegundos. Isso é muito legal se você tem um monte de coisas loucas acontecendo no seu aplicativo. Você pode configurar todos esses eventos e apenas ver a leitura de quanto tempo cada um está demorando e como eles estão interagindo. É isso para o rastreamento do sistema.
Bônus
Veja o exemplo de uma desaceleração da câmera em que podemos ver o que acontece se houver coisas de RA no aplicativo:

Aplicamos um efeito e ele foi responsável por 26,4% de todos os cálculos em cada quadro apenas para calcular um efeito. E estava diminuindo a velocidade da câmera para algo louco, como 10 quadros por segundo.
Quando entrei aqui e observei esse grande gargalo, vi que a principal coisa que fazia a maior parte do trabalho era o uso intenso do NSDispatchData.

Esta é uma subclasse de NSData. E tudo isso é obter bytes com a função range. E essa é uma função simples. Tudo o que faz é pegar alguns bytes de um dado e colocá-lo em outro lugar. Não é muito louco, mas, aparentemente, todas as coisas que ele fazia internamente estavam ocupando 18% desses 26%.
Regra nº 1É um NSData e está recebendo bytes. Isso é uma coisa simples de Objective-C, mas se você se deparar com isso e isso for um gargalo, é hora de mudar para o uso de C. Como o gargalo estava em torno de uma chamada para obter valores flutuantes, você pode apenas usar memcpy (). Com o memcpy (), você pode mover um pedaço de dados para outro lugar. Corta bastante sobrecarga.
Se você se parece com o NSData, essas classes são como milhares de linhas. Então, há muita coisa acontecendo lá. Nesse caso, temos o original em vermelho.

Aqui você obtém um intervalo, pega alguns bytes e copia-os no buffer. A versão memcpy () é quase exatamente a mesma coisa. Não parece mais complicado e faz muito menos coisas.

Quando mudamos isso e o executamos novamente, as coisas passaram de 26% para 0,6%, alterando essa linha para memcpy (). E então, a taxa de quadros aumentou dramaticamente.
Regra nº 2Evite desenhar em excesso se estiver fazendo algum tipo de aplicativo de renderização ou mesmo se estiver fazendo algo como uma barra de carregamento. Muitas vezes os eventos acontecem mais de 60 quadros por segundo. Nesse caso, você pode controlar esta atualização da interface do usuário usando um CADisplayLink. Ele possui uma propriedade chamada preferidoFramesPerSecond. Isso é apenas para iOS 10 ou superior. Para os mais antigos, você precisa fazer isso manualmente, mas ainda é útil.

Você pode definir a taxa de quadros desejada. Muitas vezes, para barras de carregamento semelhantes, definirei em torno de 15 quadros por segundo, porque isso realmente não importa. Não é necessário atualizar 60 quadros por segundo. Isso pode poupar muito trabalho que você faz se as coisas parecerem as mesmas.
Regra nº 3Use o cache do IMP. Isso é útil apenas para Objective-C. No Objetivo-C, quando você chama um método oculto, na verdade você está chamando a função de envio de mensagem Objective-C (objc_msgSend ()). Se você vê essas chamadas em traços que demoram muito tempo, é algo que você pode se livrar facilmente. É basicamente a tabela de cache em que você consulta os ponteiros de função, dando-lhe o nome de algum método. Em vez de fazer essa pesquisa todas as vezes, você pode armazenar em cache o ponteiro de função e simplesmente chamá-lo diretamente. Geralmente é pelo menos duas vezes mais rápido.

Se você não possui um ponteiro em cache, pode obtê-lo chamando methodForSelector:. Em seguida, chamamos esse método como uma chamada de função regular. Você passa o objeto no seletor e, em seguida, qualquer argumento vem depois.
Regra nº 4Não use o ARC. ARC é algo que adiciona um monte de sobrecarga. No seu código, você tem todas essas coisas acontecendo e tudo se espalha com retenções e lançamentos. Faz o que precisa e faz muito mais. Portanto, se você realmente deseja otimizar, se perceber que tem várias chamadas de retenção e liberação em seu rastreamento e elas estão demorando muito tempo, basta mudar para não usar o ARC, o que é muito mais trabalhoso.
Também é difícil convencer seus colegas de equipe a fazer isso e não ficar bravo com isso.
Não use Swift se for especialmente sensível ao desempenho. Swift é uma linguagem legal. Ele tem alguns recursos realmente legais. Mas ele também usa mais clichês dentro para obter um alto nível de funcionalidade. Se você quer ser rápido, deve ir o mais próximo possível da montagem, o mais próximo possível das coisas de baixo nível. E isso será mais rápido porque é menos código automaticamente.
Se você está olhando as coisas, se achou interessante, há um livro realmente bom chamado “iOS e MacOS: Performance Tuning”, de Marcel Weiher. Ele se aprofundou muito em muitas dessas coisas e muito mais além disso. Eu também tenho uma série de vídeos. Eu faço vídeos para RayWenderlich. Fiz uma série prática de instrumentos que é mais aprofundada e explica um pouco mais essas coisas e tem alguns exemplos. Portanto, se você quiser aprender mais sobre instrumentos especificamente, poderá assistir a essa série de vídeos. E então, vídeos da WWDC - há muitos deles que explicam coisas de desempenho diferentes como essa.
Vídeo
Aqui você encontra a primeira parte de um artigo baseado na palestra de Luke. Assista à palestra completa aqui: