Поскольку UIViewAlertForUnsatisfiableConstraints является функцией C, swizzling требует значительно больше работы, чем обычный метод Objective C.
Простейший (и то, что большинство источников говорят, что это единственный возможный способ), чтобы сделать это, можно получить с помощью субстрата MSHookFunction, однако для этого требуется телефон с джейлбрейком. Если это допустимый вариант, я бы рекомендовал использовать это, чтобы просто подключить функцию.
В среде, не связанной с джейлбрейком, требуется расширенная манипуляция во время выполнения. По какой-то причине мне очень нравится выбрасывать часы, чтобы узнать, как скоро будет мертвый язык .. так что вот полное решение! Это link было огромной помощью.
#import <dlfcn.h>
#import <objc/runtime.h>
#include <sys/mman.h>
int64_t originalOffset;
int64_t *origFunc;
void swizzled_UIViewAlertForUnsatisfiableConstraints(NSLayoutConstraint *offendingConstraint, NSArray *allConstraints) {
// inject swizzle code here
NSLog(@"swizzled!");
// call the original function (if you want!)
if (origFunc) {
// replace jump instruction w/ the original memory offset
*origFunc = originalOffset;
((void(*)(NSLayoutConstraint*, NSArray*))origFunc)(offendingConstraint, allConstraints);
}
}
static inline BOOL swizzleAlertForUnsatisfiableConstraints() {
// get the original function and hold onto it's memory offset
origFunc = dlsym(RTLD_DEFAULT, "UIViewAlertForUnsatisfiableConstraints");
if (!origFunc) {
return NO;
}
originalOffset = *origFunc;
// define the swizzled implementation
int64_t *swizzledFunc = (int64_t*)&swizzled_UIViewAlertForUnsatisfiableConstraints;
// make the memory containing the original funcion writable
size_t pageSize = sysconf(_SC_PAGESIZE);
uintptr_t start = (uintptr_t)origFunc;
uintptr_t end = start + 1;
uintptr_t pageStart = start & -pageSize;
mprotect((void *)pageStart, end - pageStart, PROT_READ | PROT_WRITE | PROT_EXEC);
//Calculate the relative offset needed for the jump instruction
//Since relative jumps are calculated from the address of the next instruction,
// 5 bytes must be added to the original address (jump instruction is 5 bytes)
int64_t offset = (int64_t)swizzledFunc - ((int64_t)origFunc + 5 * sizeof(char));
//Set the first instruction of the original function to be a jump
// to the replacement function.
//E9 is the x86 opcode for an unconditional relative jump
int64_t instruction = 0xe9 | offset << 8;
*origFunc = instruction;
return YES;
}
Просто хочу сообщить, что это выглядит многообещающим и довольно удивительным. Я собираюсь попробовать это позже сегодня вечером и отметить это как ответ, если он работает правильно. Большое спасибо за то, что вы потратили время на это. –
Я думаю, что использую это неправильно. Я получаю «Исключение: EXC_BAD_ACCESS (код = 1, адрес = 0x49656e69))». Похоже, что исходная функция правильно не вызывается. Я вижу «__swizzleAlertForUnsatisfiableConstraints_block_invoke» в трассировке стека, когда обычно вижу «AlertForUnsatisfiableConstraints». Однако похоже, что есть проблема с форматом функции замены или смещением памяти. Вы видели такую проблему? –
упс! Я тестировал это только на 32-битном симуляторе, а не на 64 бит. Если вы попробуете его на iPad 2 sim (32 бит), вы должны увидеть, что он работает. Я буду обновлять код завтра версией, которая работает как для 32/64 бит. – Casey