是否可以调整 UIViewAlertForUnsatisfiableConstraints?
Is it possible to swizzle UIViewAlertForUnsatisfiableConstraints?
我想知道 UIViewAlertForUnsatisfiableConstraints 是否是一个我可以调整的选择器。到目前为止我所知道的是 UIViewAlertForUnsatisfiableConstraints 是一个可以用来放置符号断点的符号。但是我在任何 public class 中都找不到这个符号。有人知道它在哪里吗?
请注意,我希望在单元测试中而不是在生产代码中执行此操作。
由于 UIViewAlertForUnsatisfiableConstraints 是一个 C 函数,因此调配它需要比典型的 Objective C 方法多得多的工作。
最简单的(也是大多数消息来源所说的唯一可能的方法)是通过 Substrate 的 MSHookFunction,但是这需要越狱 phone。如果这是一个有效的选项,我建议使用它来简单地挂接函数。
在非越狱环境中,需要高级运行时操作。出于某种原因,我真的很喜欢浪费时间学习一门即将消失的语言。所以这里是完整的解决方案!这 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;
}
我尝试了 Casey 的解决方案并且工作正常,但仅限于模拟器。
ARM 处理器有 Thumb 模式,所以使用 ARM B 指令比 x86 JMP 更难。
UIViewAlertForUnsatisfiableConstraints 从以下 UIView 方法调用。好像是在内部引擎的delegate里面定义的。
- (void)engine:(NSISEngine *)arg1 willBreakConstraint:(id <NSISConstraint>)arg2 dueToMutuallyExclusiveConstraints:(NSArray *)arg3;
调配 ObjC 方法比 C 函数容易得多。
定义私有接口:
@interface UIView (UIConstraintBasedLayout_EngineDelegate_PrivateInterface)
- (void)engine:(id /* NSISEngine */)engine willBreakConstraint:(NSLayoutConstraint *)breakConstraint dueToMutuallyExclusiveConstraints:(NSArray<NSLayoutConstraint *> *)mutuallyExclusiveConstraints;
@end
然后swizzle objc方法:
+ (void)swizzleAutoLayoutAlertMethod
{
Class class = [UIView class];
Method originalMethod = class_getInstanceMethod(class, @selector(engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:));
if (!originalMethod) {
[NSException raise:NSInternalInconsistencyException format:@"This platform does not support Auto Layout or interface has been changed."];
}
Method swizzledMethod = class_getInstanceMethod(class, @selector(swizzled_engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:));
method_exchangeImplementations(swizzledMethod, originalMethod);
}
注意:我用这个想法为自动布局创建了 lint 库,参考 https://github.com/ypresto/AutoLayoutLint .
我想知道 UIViewAlertForUnsatisfiableConstraints 是否是一个我可以调整的选择器。到目前为止我所知道的是 UIViewAlertForUnsatisfiableConstraints 是一个可以用来放置符号断点的符号。但是我在任何 public class 中都找不到这个符号。有人知道它在哪里吗?
请注意,我希望在单元测试中而不是在生产代码中执行此操作。
由于 UIViewAlertForUnsatisfiableConstraints 是一个 C 函数,因此调配它需要比典型的 Objective C 方法多得多的工作。
最简单的(也是大多数消息来源所说的唯一可能的方法)是通过 Substrate 的 MSHookFunction,但是这需要越狱 phone。如果这是一个有效的选项,我建议使用它来简单地挂接函数。
在非越狱环境中,需要高级运行时操作。出于某种原因,我真的很喜欢浪费时间学习一门即将消失的语言。所以这里是完整的解决方案!这 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;
}
我尝试了 Casey 的解决方案并且工作正常,但仅限于模拟器。 ARM 处理器有 Thumb 模式,所以使用 ARM B 指令比 x86 JMP 更难。
UIViewAlertForUnsatisfiableConstraints 从以下 UIView 方法调用。好像是在内部引擎的delegate里面定义的。
- (void)engine:(NSISEngine *)arg1 willBreakConstraint:(id <NSISConstraint>)arg2 dueToMutuallyExclusiveConstraints:(NSArray *)arg3;
调配 ObjC 方法比 C 函数容易得多。
定义私有接口:
@interface UIView (UIConstraintBasedLayout_EngineDelegate_PrivateInterface)
- (void)engine:(id /* NSISEngine */)engine willBreakConstraint:(NSLayoutConstraint *)breakConstraint dueToMutuallyExclusiveConstraints:(NSArray<NSLayoutConstraint *> *)mutuallyExclusiveConstraints;
@end
然后swizzle objc方法:
+ (void)swizzleAutoLayoutAlertMethod
{
Class class = [UIView class];
Method originalMethod = class_getInstanceMethod(class, @selector(engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:));
if (!originalMethod) {
[NSException raise:NSInternalInconsistencyException format:@"This platform does not support Auto Layout or interface has been changed."];
}
Method swizzledMethod = class_getInstanceMethod(class, @selector(swizzled_engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:));
method_exchangeImplementations(swizzledMethod, originalMethod);
}
注意:我用这个想法为自动布局创建了 lint 库,参考 https://github.com/ypresto/AutoLayoutLint .