Отладка EXC_BAD_ACCESS в Mac OS X и iPhone

    Привет! В этой статье я постараюсь рассказать о том, как можно облегчить себе жизнь при отладке iPhone- и Mac-приложений. Речь пойдет об отладке исключения EXC_BAD_ACCESS, одного из самых неприятных в природе.

    Исключение EXC_BAD_ACCESS возникает, когда мы пытаемся послать сообщение объекту, который уже был освобожден (released). Ситуация обычно осложняется тем, что к тому моменту, когда выскакивает ошибка, стэк вызова уже не содержит информацию, которая могла бы нам помочь. То есть, непонятно, какой конкретно объект был освобожден. И, тем более, непонятно, в каком месте кода он был освобожден :). Еще веселее становится, если приложение многопоточное.

    К счастью, ребята из Apple приложили все усилия, чтобы нам помочь. Они создали класс-заглушку NSZombie. Если включить его поддержу (как это сделать — расскажу ниже), то после удаления каждого объекта на его месте (т.е. по его адресу в памяти) будет жить «зомби» и принимать сообщения, что позволит при отладке определить, какой объект был освобожден и устранить ошибку.

    Итак, начнем. В один прекрасный день ваше iPhone- или Mac-приложение начало вылетать и в консоли отладчика вы видите следующее сообщение:

    Program received signal: “EXC_BAD_ACCESS”.

    И, собственно, больше ничего. Стэк вызова, скорее всего, тоже вам ничем не помог.

    В окне Groups & Files (слева в xCode) найдите ветку Executables. Щелкните правой кнопкой на исполняемом файле вашего приложения и в выпадающем меню выберите пункт Get Info. В появившемся окне откройте вкладку Arguments и добавьте две переменные окружения: NSZombieEnabled и MallocStackLoggingNoCompact. Установите NSZombieEnabled значение YES, а MallocStackLoggingNoCompact1. Как это должно выглядеть, показано на скриншоте:

    image

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

    2010-01-25 14:35:24.840 MyApplication[1393:20b] *** -[CFString retain]: message sent to deallocated instance 0x42a5060

    Это значит, что когда-то у вас жил объект класса CFString по адресу 0x42a5060. Он уже был освобожден (released), а ваша программа не унимается и пытается послать ему сообщение retain. Осталось найти этот объект и найти в коде место, где был вызван лишний release. Либо не вызван retain, зависит от конкретной ситуации :)

    Вам поможет следущее: в консоли отладчика наберите такую команду:

    shell malloc_history 1393 0x42a5060

    Вместо 1393 вам следует вписать свой идентификатор процесса (PID). Его вы можете подсмотреть в консольном сообщении об ошибке сразу после имени приложения. А вместо 0x42a5060 впишите адрес объекта, который был освобожден (тоже можно посмотреть в сообщении об ошибке).

    В результате вашим глазам предстанет страшная картина вроде этой:

    image

    Не пугайтесь, все не так страшно, как может показаться на первый взгляд. Ближе к низу ищите имена классов и методов, которые присутствуют в вашем коде. То есть, написаны вами. Где-то там и притаился злочастный вызов release. Ищите и исправляйте.

    P.S. Краем глаза я где-то прочитал, что можно отлавливать подобные ошибки с помощью Instruments / ObjectAlloc. Был бы очень признателен, если бы кто-то дал мне на водку… Ой, наводку
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 25
    • –2
      Первая мысль — где-то я уже это видел. И ведь действительно видел — вот здесь.
      • 0
        Это статья моего друга, т.к. у него нет возможности опубликовать ее тут, он попросил меня
      • 0
        На CocoaDev много подобных полезных сниппетов
        • 0
          Полезно, я знал что EXC_BAD_ACCESS это обращение к не существующей переменной, но как его быстро…
          Спасибо!
          • 0
            Спасибо, полезная статья.
            • –1
              Умилило предложение: «К счастью, ребята из Apple приложили все усилия, чтобы нам помочь»…
              2 года занимаюсь разработкой для iPhone'а и не видел более ужасного и не информативного дебаггера (по крайней мере без танцов с бубном и вписывания каких-то хитрых параметров), чем тот, что в XCode. Ад.
              • 0
                мне казалось, что там GDB
                • 0
                  А я и не говорил, что там какой-то свой дебаггер, я лишь написал «тот, что используется в XCode».
                • –1
                  дебаггер там самый привычный – gdb. А вот оболочка по сравнению в дебаггером в VS для .Net конечно сосет
                  • +2
                    Уже 2 года пишу под ифоны. Проблем с дебагом не возникало…
                    Разве что в случаях когда я юзаю помесь c++ и Objective-c — то чтобы gdb понял с каким языком я работаю — приходится писать в консоли чтонибудь типа: set language c++
                    А вообще среда разработки XCode tools — это лучшее что я видел (тем более что еще и без дополнительных затрат, кроме покупки мака).
                    • 0
                      Ну, виртуальные машины никто не отменял ;)
                  • –2
                    Спасибо!

                    Для меня (балуюсь на java для Android в качестве хобби) это как сказка про Бармалея: такие светлые статьи лишний раз напоминают насколько всё лушче в современных языках программирования и средах исполнения.

                    Статья вызвала во мне новый виток любви к JVM, Андроиду, open-souce, JetBrains и вообще к человечеству!
                    • 0
                      А какие языки вы считаете несовременными?
                      • –1
                        такие, где подобные проблемы не возникают в принципе, by deisgn.
                        ну и чтоб поддержка ide была.

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

                        Хочешь сделать человека счастливым? Сделай ему плохо, а потом верни назад. Таково отношение эппла к программистам.
                    • +1
                      мне вот это помогло, ты как раз спрашивал) www.youtube.com/watch?v=LQtPr8bkB3g
                      • 0
                        Спасибо!
                        • 0
                          Обновите пост — сэкономте время людям.
                          • 0
                            и еще вот доп. инфо:
                            установка MallocStackLoggingNoCompact приводит к тому что дебагер для устройств с 3.1.3 прошивкой не запускается. Выдает «mi_cmd_stack_list_frames not enough frames» при запуске.
                            Убираем этот флаг — и все сразу запускается.
                            Для 4.2 устройства такой проблемы этот флаг не вызывает.

                            Ваш пост первый по запросу NSZombie в рунете — добавте инфо в пост — сэкономте людям время.
                      • 0
                        Спасибо, полезная информация.
                        • 0
                          К сожалению, это не всегда помогает. Если работать напрямую с С-style указателями, malloc/free, то зомби не дают никакой выгоды. Правда, есть еще несколько флагов:

                          MallocBadFreeAbort
                          MallocScribble
                          MallocStackLogging

                          (использовать их нужно также)

                          Что кто делает — можно попробовать догадаться по названию. Ну, и, по опыту ковыряния с Runtime, действительно помогают.
                          • +1
                            Большое спасибо — очень помогло
                            • 0
                              EXC_BAD_ACCESS в общем случае возникает при попытке доступа к области памяти, куда у тебя доступа быть не должно. Уже освобожденный объект — лишь частный случай.

                              Вчера тут пытался логировать прием информации с сервера

                              NSData *data =…
                              NSLog(@«data received, length now is: %@», data.length);

                              Полдня отлаживался. Опытные товарищи уже знают, в чем тут ошибка :-)
                              • 0
                                И да, зомби тут не поможет. Приложение просто дропается и всё :-)
                                • 0
                                  В чём же ошибка?
                                  • 0
                                    1) Немного странно NSUInteger выводить через %@
                                    2) Наверное, не плохо бы проверить на !data
                                    ?

                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.