Есть ли способ перечислить все методы Swizzled в приложении для iOS?
По сути, я ищу способ определить, когда / какие сторонние библиотеки переполнены. Недавно я столкнулся с ситуацией, когда в рекламной библиотеке использовался странный форк AFNetworking. AFNetworking кружит NSURLSessionTask, и эти два кружка не играли хорошо при определенных обстоятельствах. Мне бы очень хотелось иметь возможность обнаруживать и проверять подобные вещи, а в идеале даже сохранять версионный дамп каждого изворотливого метода в приложении, чтобы у нас было некоторое представление о том, кто из мартышек исправляет, что и каковы риски. Google и поиск переполнения стека не нашли ничего, кроме как кучу уроков о том, как быстро справиться. Кто-нибудь сталкивался с этой проблемой или есть решение? Похоже, я мог бы что-то кодировать с помощью objc/runtime.h, но я не могу себе представить, что я первый, кому это нужно.
1 ответ
Вот самое близкое, что мне удалось получить, с несколькими часами переделок. Он включает в себя использование форка mach_override, пару изысков DYLD относительно порядка загрузки и желудок для сумасшедших хаков.
Он будет работать только на симуляторе, но этого должно быть достаточно для варианта использования, который у вас есть (я, конечно, надеюсь, что у вас нет зависимостей только от устройства).
Мясо кода выглядит примерно так:
#include <objc/runtime.h>
#include <mach_override/mach_override.h>
// It is extremely important that we have DYLD run this constructor as soon as the binary loads. If we were to hook
// this at any other point (for example, another category on NSObject, in the main application), what could potentially
// happen is other `+load` implementations get invoked before we have a chance to hook `method_exchangeImplementation`,
// and we don't get to see those swizzles.
// It is also extremely important that this exists inside its own dylib, which will be loaded by DYLD before _main() is
// initialized. You must also make sure that this gets loaded BEFORE any other userland dylibs, which can be enforced by
// looking at the order of the 'link binary with libraries' phase.
__attribute__((constructor))
static void _hook_objc_runtime() {
kern_return_t err;
MACH_OVERRIDE(void, method_exchangeImplementations, (Method m1, Method m2), &err) {
printf("Exchanging implementations for method %s and %s.\n", sel_getName(method_getName(m1)), sel_getName(method_getName(m2)));
method_exchangeImplementations_reenter(m1, m2);
}
END_MACH_OVERRIDE(method_exchangeImplementations);
MACH_OVERRIDE(void, method_setImplementation, (Method method, IMP imp), &err) {
printf("Setting new implementation for method %s.\n", sel_getName(method_getName(method)));
method_setImplementation_reenter(method, imp);
}
END_MACH_OVERRIDE(method_setImplementation);
}
Который на удивление прост и выдает результат примерно так:
Обмен реализациями для описания метода и custom_description.
Нет хорошего способа (без использования точки останова и просмотра трассировки стека) выяснить, какой класс подвергается Swizzling, но в большинстве случаев просто взглянуть на селекторы должны дать вам подсказку, куда идти дальше.
Похоже, что он работает с парой категорий, которые я создал +load
и из моего прочтения mach_override
и внутренности DYLD, если у вас правильно настроен порядок загрузки библиотеки, вы можете ожидать, что он будет инициализирован раньше, чем любой другой код пользователя, если вы поместите его в свою собственную динамическую библиотеку.
Теперь я не могу ручаться за безопасность этого, но кажется полезным использовать его как инструмент отладки, поэтому я опубликовал мой пример на github:
https://github.com/richardjrossiii/mach_override_example
Это лицензия MIT, так что не стесняйтесь использовать по своему усмотрению.