Метод подмены API-интерфейса частного фреймворка для iOS

Я знаю, что существует бесчисленное множество ресурсов по использованию методов. Однако можно ли использовать метод из частного API? Проблема в том, что нет файлов заголовков. Я хотел бы использовать метод частного класса в PrivateFramework, например (случайный пример) методы Message.framework

Это для личного тестирования, я понимаю, что Apple забросит его.


person John Smith    schedule 27.06.2014    source источник
comment
Если вы когда-нибудь пытаетесь что-то сделать с частными API, когда вам кажется, что вам нужны файлы заголовков, вы можете просто перепроектировать их. Изучите такие инструменты, как class-dump или class-dump-z. Многие люди также сами разместили в Интернете переработанные заголовки. Затем просто включите этот заголовок в качестве исходного файла в свой проект. Если у вас возникнут проблемы с некомпилированными реконструированными заголовками, часто вам просто нужно вручную проверить их и удалить #imports, которые вам не нужны, или просто сократить заголовочный файл, чтобы включить только ту функцию, которая вас интересует. .   -  person Nate    schedule 29.06.2014
comment
Это то, что я делал изначально и думал, что должен быть более простой способ. В любом случае, я могу использовать этот частный API благодаря Брайану (см. Ниже). Проблема в том, что другие фреймворки не умеют раскручиваться. т.е. если я вызываю этот метод непосредственно из моего тестового кода, я получаю функцию swizzled, но если этот метод вызывается в какой-либо другой структуре, он не вызывает мою функцию swizzled.   -  person John Smith    schedule 29.06.2014
comment
Возможно, вам потребуется задать новый вопрос. Кроме того, будьте конкретны. Было бы полезно узнать, какой API вы используете, и вы говорите, что когда этот API вызывается из процесса вашего приложения, но вызывается непосредственно фреймворком (а не вашим кодом), тогда swizzling не работает?   -  person Nate    schedule 30.06.2014


Ответы (2)


Вы можете использовать NSClassFromString, чтобы получить Class, и использовать библиотеку времени выполнения для переключения методов. Заголовочные файлы не требуются. Вам просто нужно знать имя класса и подпись метода.

sel_getUid можно использовать, когда @selector(somePrivateMethod) выдает ошибку о том, что somePrivateMethod недопустимый селектор (поскольку заголовок недоступен)

Код взят из моего плагина Xcode

SEL sel = sel_getUid("codeDiagnosticsAtLocation:withCurrentFileContentDictionary:forIndex:");
Class IDEIndexClangQueryProviderClass = NSClassFromString(@"IDEIndexClangQueryProvider");

Method method = class_getInstanceMethod(IDEIndexClangQueryProviderClass, sel);
IMP originalImp = method_getImplementation(method);

IMP imp = imp_implementationWithBlock(^id(id me, id loc, id dict, IDEIndex *idx) {
    id ret = ((id (*)(id,SEL,id,id,id))originalImp)(me, sel, loc, dict, idx);

    // do work

    return ret;
});

method_setImplementation(method, imp);
person Bryan Chen    schedule 27.06.2014
comment
что случилось? также убедитесь, что этот код действительно запустился ... поместите его в +[load] - person Bryan Chen; 27.06.2014
comment
Код определенно запущен. Однако, чтобы убедиться в этом, я могу вызвать частный API и посмотреть, вызывает ли он исходный метод или выделенный. Я попробовал это и получил: неопознанный селектор отправлен в класс, что означает, что мой частный api вообще не вызывается. Я использовал объявленный класс, например: [IDEIndexClangQueryProviderClass codeDiagnosticsAtLocation: 0 withCurrentFileContentDictionary: 0 forIndex: NULL] - person John Smith; 28.06.2014
comment
подожди ... почему ты пытаешься позвонить [IDEIndexClangQueryProviderClass codeDiagnosticsAtLocation:0 withCurrentFileContentDictionary:0 forIndex:NULL]? это просто пример. если вы также не пишете плагин Xcode, он не будет работать. вы предполагаете изменить имя класса, имя селектора и сигнатуры методов на то, что существует на ios. - person Bryan Chen; 28.06.2014
comment
Обновление. Нет, я звонил своему собственному приватному API, и это сработало. Во всяком случае, я понял, что метод, который я пытался использовать, является методом экземпляра, поэтому вместо [Class_I_chose_above args: NULL] я должен использовать allocate / init экземпляр, который работал :) Спасибо! - person John Smith; 28.06.2014
comment
Дополнение к вашему ответу: вы также можете использовать NSSelectorFromString() вместо sel_getUid, чтобы захватить селектор, при этом обходя проверки компилятора. Я нахожу NSSelectorFromString немного более доступным для взаимодействия со Swift - person Charlton Provatas; 07.04.2018

Создайте категорию для класса и добавьте объявление для метода, который вы хотите вызвать. Затем вы можете просто создать экземпляр класса и вызвать метод.

Это также работает для частных методов модульного тестирования в вашем коде.

person zaph    schedule 27.06.2014
comment
Я не пытаюсь добавить метод, я пытаюсь заменить метод. Вы подразумеваете, что объявляете, что этот метод заменит существующий? - person John Smith; 27.06.2014
comment
Объявление метода предоставит доступ к методу для его изменения. В вопросе жалоба заключалась в том, что не было метода заголовка. Создав категорию, вы можете эффективно создать метод заголовка для метода, который хотите изменить. - person zaph; 27.06.2014
comment
Здесь нет причин использовать категории, и поскольку на самом деле категории предназначены не для этого, это сбивающий с толку метод, которого следует избегать. Если проблема в том, что фреймворк является частным и у вас нет заголовков, просто перепроектируйте заголовок (или отдельные методы, которые вас интересуют) и включите этот заголовок в свой проект. Нет причин для того, чтобы быть категорией. В OP отсутствует фундаментальный метод разработки частных API, который заключается в обратном проектировании заголовков, которые вам нужны. Это стандартный образец для этого класса проблем. - person Nate; 29.06.2014
comment
Может быть, класс является общедоступным и имеет общедоступный заголовок, но метод является частным. Что касается использования категорий, я согласен, что их следует избегать. Целый обмен частным классом, как правило, плохая идея, но в этом случае OP заявляет: для личного тестирования. - person zaph; 29.06.2014