Узнайте о зомби-объектах в Swift, Address Sanitizer и Thread Sanitizer и устраните эти неприятные сбои

Поначалу создание приложений идет хорошо. Вы разрабатываете приложение с нуля, оно стабильно и работает нормально. После выпуска первой версии вы получите первое представление о возникающих сбоях, и один из них помечен как ошибка EXC_BAD_ACCESS. Путешествие по выяснению того, что происходит, начинается здесь.

Первая задача - воспроизвести аварию, что может оказаться трудным. Когда вы это сделаете, выяснить причину и устранить ее станет другой задачей.

Что означает EXC_BAD_ACCESS?

EXC_BAD_ACCESS - это исключение, возникшее в результате доступа к плохой памяти. Мы постоянно работаем с указателями на память в Swift, которые ссылаются на определенный адрес памяти. Приложение выйдет из строя всякий раз, когда мы попытаемся получить доступ к указателю, который недействителен или больше не существует. Такой указатель также известен как «висячий указатель».

Основная причина такой плохой памяти может быть разной. Ссылочная память может быть удалена или освобождена, в то время как значение указателя никогда не обновляется. Указатель по-прежнему указывает на ячейку освобожденной памяти - он становится висящим указателем.

Как устранить исключения из-за плохого доступа

Ошибка EXC_BAD_ACCESS в Swift часто разочаровывает. Непосредственно может быть непонятно, в чем причина сбоя. Однако, если вы знаете возможные причины, вполне возможно быстро найти первопричину исключения из-за плохого доступа.

Если вы не знаете, как воспроизвести аварию

Сбой трудно исправить, если вы не знаете, как его воспроизвести - это означает, что вы не можете проверить, действительно ли вы его исправили. Возможно, вы знаете, где происходит сбой, но не можете найти такой же сбой в своем приложении.

Убедитесь, что вы запускаете ту же конфигурацию сбоя. Установите правильную версию приложения на ту же iOS и устройство, если это возможно. Это, по крайней мере, исключает невозможность воспроизведения сбоя из-за несоответствия конфигурации.

Если вам по-прежнему не удается воспроизвести сбой, попробуйте одно из следующих действий:

Использование Address Sanitizer

Как только вы сможете воспроизвести сбой локально, самое время выполнить несколько проверок, чтобы убедиться, что ваш код не содержит проблем с памятью.

Address Sanitizer, также известный как ASan, обнаруживает повреждения памяти и другие ошибки памяти во время выполнения. Apple даже рекомендует постоянно использовать ASan во время разработки:

Проблемы с памятью часто являются причиной уязвимостей системы безопасности. Мы настоятельно рекомендуем вам включить Address Sanitizer в процесс аттестации вашего программного обеспечения.

Address Sanitizer можно включить в настройках схемы в разделе диагностики:

После включения вы снова начинаете воспроизводить сбой. Address Sanitizer приостанавливает работу приложения всякий раз, когда обнаруживает проблему с памятью. Посмотрите, обнаруживает ли он какие-либо проблемы с памятью, устраните их и проверьте, возникает ли еще EXC_BAD_ACCESS.

Плохой доступ из-за условий гонки

Гонки данных могут быть еще одной причиной плохого доступа к памяти. Поэтому стоит убедиться, что в вашем приложении нет проблем с потоками. Для этого вы можете использовать Thread Sanitizer, что подробно описано в моем сообщении в блоге Thread Sanitizer объяснил: гонки данных в Swift.

Как насчет зомби-объектов?

Разработчики, имеющие опыт работы с Objective-C и исключениями плохого доступа, могут подумать об объектах Zombie для решения проблемы с памятью. Объекты-зомби можно включить с помощью тех же настроек диагностики, которые мы использовали ранее для включения Address Sanitizer. Он поддерживает освобожденные объекты, что может помочь найти основную причину проблемы с памятью, если она связана с доступом к освобожденным экземплярам.

По сравнению с Objective-C Swift делает гораздо больше для предотвращения проблем с памятью. Более вероятно, что вы столкнетесь со сбоями, такими как «Попытка прочитать незарегистрированную ссылку», чем с исключением неправильного доступа, как в Objective-C для доступа к освобожденным экземплярам. В этих случаях вы должны включить объекты зомби, чтобы вы по-прежнему могли читать адрес освобожденной памяти, к которой осуществляется доступ.

Более вероятно, что вы столкнетесь со сбоями, связанными с памятью, если используете в Swift неуправляемые и небезопасные указатели. Взаимодействие с фреймворками Objective-C также может привести к более частому возникновению этих сбоев. Если это так, попробуйте снова воспроизвести сбой с включенными объектами-зомби и посмотрите, дает ли это больше информации для устранения сбоя.

Заключение

Хотя EXC_BAD_ACCESS встречается в Swift намного реже, чем в Objective-C, это все еще частый сбой. Надеюсь, после прочтения этой статьи вы сможете решить эти проблемы намного проще.

Если вы хотите еще больше улучшить свои знания Swift, посетите страницу категории Swift.

Спасибо!