gpt4 book ai didi

ios - 使用可变参数调配方法并转发消息 - Bad Access

转载 作者:可可西里 更新时间:2023-11-01 04:41:59 25 4
gpt4 key购买 nike

我正在实现一个“代码注入(inject)器类”,通过方法调配可以让你有可能做这样的事情:

FLCodeInjector *injector = [FLCodeInjector injectorForClass:[self class]];
[injector injectCodeBeforeSelector:@selector(aSelector:) code:^{
NSLog(@"This code should be injected");
}];

aSelector 可以是具有可变数量参数和可变返回类型的方法。参数/和返回类型可以是对象或原始类型。

首先,我附上 injectCodeBeforeSelector: 的代码,让您了解我在做什么(我删除了代码中不感兴趣的部分):

- (void)injectCodeBeforeSelector:(SEL)method code:(void (^)())completionBlock
{

NSString *selector = NSStringFromSelector(method);

[self.dictionaryOfBlocks setObject:completionBlock forKey:selector];

NSString *swizzleSelector = [NSString stringWithFormat:@"SWZ%@", selector];

// add a new method to the swizzled class
Method origMethod = class_getInstanceMethod(self.mainClass, NSSelectorFromString(selector));
const char *encoding = method_getTypeEncoding(origMethod);

[self addSelector:NSSelectorFromString(swizzleSelector) toClass:self.mainClass methodTypeEncoding:encoding];
SwizzleMe(self.mainClass, NSSelectorFromString(selector), NSSelectorFromString(swizzleSelector));

}

-(void)addSelector:(SEL)selector toClass:(Class)aClass methodTypeEncoding:(const char *)encoding
{
class_addMethod(aClass,
selector,
(IMP)genericFunction, encoding);
}

基本上,我使用 class_addMethod 将 fake/swizzle 方法添加到目标类,然后执行 swizzle。该方法的实现设置为如下函数:

id genericFunction(id self, SEL cmd, ...) {
// call the block to inject
...
// now forward the message to the right method, since method are swizzled
// I need to forward to the "fake" selector SWZxxx

NSString *actualSelector = NSStringFromSelector(cmd);
NSString *newSelector = [NSString stringWithFormat:@"SWZ%@", actualSelector];
SEL finalSelector = NSSelectorFromString(newSelector);

// forward the argument list
va_list arguments;
va_start ( arguments, cmd );

return objc_msgSend(self, finalSelector, arguments);
}

现在的问题是:我在最后一行有一个 EXC_BAD_INSTRUCTION ( objc_msgSend_corrupt_cache_error ())。如果我将 va_list 参数转发给假选择器,就会出现问题。如果我将最后一行更改为

return objc_msgSend(self, cmd, arguments);

没有错误,但显然开始了无限递归。

我尝试过:

  • 使用 va_copy
  • 在发送消息之前移除 swizzle

但没有结果。我认为问题与以下事实有关:va_list 不是一个简单的指针,它可以类似于相对于方法堆栈地址的偏移量。因此,我无法使用另一个函数(非混合函数)的 arg 列表调用函数(混合函数)的 objc_msgsend。

我试图改变方法并复制 NSInvocation 中的所有参数,但我在管理调用的返回值时遇到了其他问题,甚至一个接一个地复制参数(管理所有不同类型)需要大量代码,所以我更喜欢回到这种方法,这对我来说似乎更干净(恕我直言)

你有什么建议吗?谢谢

最佳答案

这里的主要问题是如何将变量参数传递给函数。

通常,它们在堆栈上传递,但据我所知,ARM ABI 不是这种情况,至少在可能的情况下使用寄存器。

所以这里有两个问题。
首先,编译器可能会在执行您自己的方法的代码时弄乱这些寄存器。
我不确定这一点,因为我对 ARM ABI 了解不多,所以您应该查看引用资料。

第二个问题,更重要的是,您实际上是将单个变量参数传递给 obj_msgSend (va_list)。所以目标方法显然不会收到它所期望的。

想象一下:

void bar( int x, ... )
{}

void foo( void )
{
bar( 1, 2, 3, 4 );
}

在 ARM 上,这意味着,对于 foo功能:

movs    r0, #1
movt r0, #0
movs r1, #2
movt r1, #0
movs r2, #3
movt r2, #0
movs r3, #4
movt r3, #0
bl _bar

变量参数在 R1 中传递, R2R3 , 和 int R0 中的参数.

因此在您的情况下,作为对 objc_msdSend 的调用用于调用您的方法,R0应该是目标对象的指针,R1选择器的指针和变量参数应该从 R2 开始.

当您自己调用 objc_msdSend 时, 你至少覆盖了 R2 的内容, 和你的 va_list .

你应该尽量不关心可变参数。运气好的话,如果 objc_msgSend 之前的代码调用(您获得最终选择器的地方)不会弄乱这些寄存器,正确的值应该仍然存在,使它们可用于目标方法。

这当然只适用于真实设备,而不适用于模拟器(模拟器是 x86,因此这里的可变参数在堆栈上传递)。

关于ios - 使用可变参数调配方法并转发消息 - Bad Access,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18115237/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com