我正在实现一个“代码注入器类”,通过方法调配可以让你有可能做这样的事情:
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 中的所有参数,但我在管理调用的返回值时遇到了其他问题,甚至一个一个地复制参数(管理所有不同的类型)需要大量代码,所以我更喜欢返回对于这种方法,这对我来说似乎更清洁(恕我直言)
你有什么建议吗?谢谢