Как зарегистрировать все методы, используемые в приложении iOS

Я занимаюсь разработкой приложения для iPad для клиента. Уже проделана значительная работа, и я пытаюсь собрать воедино, как все это работает.

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

Какой лучший способ регистрации методов?

9 ответов

Решение

Вдохновленный ответом tc на подобный вопрос здесь, я собрал действие отладочной точки останова, которое будет регистрировать имя класса и метода при каждом запуске objc_msgSend() в вашем приложении. Это работает аналогично сценарию DTrace, который я описал в этом ответе.

Чтобы включить это действие точки останова, создайте новую символическую точку останова (в Xcode 4 перейдите к навигатору точек останова и создайте новую символическую точку останова, используя плюс в левом нижнем углу окна). Иметь символ быть objc_msgSendустановите автоматическое продолжение после оценки действий и установите действие в качестве команды отладчика, используя следующее:

printf "[%s %s]\n", (char *)object_getClassName(*(long*)($esp+4)),*(long *)($esp+8)

Ваша точка останова должна выглядеть примерно так:

Действие точки останова

Это должно выйти из таких сообщений при запуске с вашим приложением:

[UIApplication sharedApplication]
[UIApplication _isClassic]
[NSCFString getCString:maxLength:encoding:]
[UIApplication class]
[SLSMoleculeAppDelegate isSubclassOfClass:]
[SLSMoleculeAppDelegate initialize]

Если вам интересно, где я вытащил адреса памяти, прочитайте эту статью Phrack о внутреннем устройстве Objective-C. Приведенные выше адреса памяти будут работать только на симуляторе, поэтому вам может потребоваться настроить его для работы с приложениями на устройствах iOS. Коллин предлагает следующую модификацию в своем ответе, чтобы запустить это на устройстве:

printf "[%s %s]\n", (char *)object_getClassName($r0),$r1

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

Если вы используете LLDB, вам нужно будет использовать следующие команды отладчика. Они были протестированы в Xcode 4.6.

Прибор:

expr -- (void) printf("[%s %s]\n", (char *)object_getClassName($r0),$r1)

Тренажер:

expr -- (void) printf("[%s %s]\n", (char *)object_getClassName(*(long*)($esp+4)), *(long *)($esp+8))

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

expr -- (void) printf("[%s %s]\n", (char *)object_getClassName($arg1),$arg2)

Подход Брэда Ларсона можно адаптировать для запуска на устройстве с помощью команды отладчика:

printf "[%s %s]\n", (char *)object_getClassName($r0),$r1

Более подробную информацию можно найти в Техническом примечании здесь: technotes

Более поздние версии XCode вам нужно называть так

expr -- (void)printf("[%s, %s]\n",(char *) object_getClassName(*(long*)($esp+4)), (char *) *(long *)($esp+8) )

Если вы хотите ограничить вывод только сообщениями, отправленными одному классу, вы можете добавить условие, подобное этому

(int)strcmp((char*)object_getClassName($r0), "NSString")==0

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

expr -- (void) printf("[%s %s]\n", (char *)object_getClassName($rdi), (char *) $rsi)

Или, если это не сработает, запишите это так:

введите описание изображения здесь

Основная идея - использовать $rdi для объекта (self) и $rsi для селектора.

Один из разработчиков научил меня добавлять те же два оператора журнала в каждый метод. Одна как первая строка, другая как последняя строка. Я думаю, что у него есть скрипт, который делает это автоматически для своих проектов, но результат:

NSLog(@"<<< Entering %s >>>", __PRETTY_FUNCTION__);
NSLog(@"<<< Leaving %s >>>", __PRETTY_FUNCTION__);

На консоли это будет выплевывать что-то вроде:

 <<< Entering -[MainListTableViewController viewDidLoad] >>>

Очень помогает в отслеживании того, что происходит.

NSLog(@"%@", NSStringFromSelector(_cmd));

ИЛИ ЖЕ

NSLog(@"%s", __PRETTY_FUNCTION__);
Другие вопросы по тегам