我正在编写一个 objc 桥,我发现了一种使用 objc_msgSend 调用 objc 方法的非常有效的方法。
基本上,该代码能够生成一个宏,该宏将传递给 objc_msg 从 NSArray 发送正确数量的参数(需要 metamacros.h)。
#import "metamacros.h"
#define CFIEXTRACTARGS(COUNT, ARR) \
, ARR[COUNT] \
#define objc_call(RECIEVER, SELECTOR, COUNT, ARR) \
objc_msgSend(RECIEVER, SELECTOR \
metamacro_for_cxt(COUNT, CFIEXTRACTARGS,, ARR) \
) \
一切都按预期工作并且非常好,但不幸的是我刚刚发现由于 Apple 在最近的运行时更改,如果没有显式转换为正确的函数指针,就无法直接调用 objc_msgSend。
发件人:https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaTouch64BitGuide/ConvertingYourAppto64-Bit/ConvertingYourAppto64-Bit.html
:“虽然消息函数的原型(prototype)是可变参数形式,但Objective-C运行时调用的方法函数并不共享同一个原型(prototype)。Objective-C运行时直接调度到实现该方法的函数,所以调用约定不匹配,如前所述。因此,您必须将 objc_msgSend 函数转换为与被调用方法函数匹配的原型(prototype)。"
例子:
- (int) doSomethingint) x { ... }
- (void) doSomethingElse {
int (*action)(id, SEL, int) = (int (*)(id, SEL, int)) objc_msgSend;
action(self, @selector(doSomething, 0);
}
显然我不能为每个函数组合创建一个强制转换,所以我想知道什么是有效的替代方案。 NSInvocation 真的太慢了。
Best Answer-推荐答案 strong>
使用 NSArray 作为参数有点困惑,这肯定意味着它们都必须是对象类型?
但是,除了这个问题,没有必要停止使用基于宏的方法,只需添加强制转换作为参数:
#define objc_call(RECIEVER, TYPE, SELECTOR, COUNT, ARR) \
(TYPE objc_msgSend)(RECIEVER, SELECTOR \
metamacro_for_cxt(COUNT, CFIEXTRACTARGS,, ARR) \
)
并按如下方式使用:
- (NSString *) doSomethingNSString *) x { ... }
(注意使用 NSString - 一种对象类型,与问题中的 int 不同)
- (void) doSomethingElseToo
{
NSArray *args = @[ @"too" ];
NSString *res = objc_call(self, (NSString * (*)(id, SEL, NSString *)), @selector(doSomething, 1, args);
NSLog(@"res = %@", res);
}
HTH
附录
@RichardJ.RossIII 在评论中建议他认为您希望完全避免提供任何类型转换;并不是说您希望避免针对不同类型使用不同的宏,我认为是这种情况并在上面提供了解决方案。
鉴于您希望避免 NSInvocation 让我们再次查看您的宏。如前所述,它使用 NSArray 从中提取参数,因此每个参数都是一个对象,即 id 类型。如果所有参数都属于同一类型,则可能的强制转换数量会大大减少。
我们已经考虑了参数,您希望支持多少种返回类型?让我们暂时假设它是一个,id 。以下两个宏将完成最多 19 个参数的工作:
#define metamacro_cast(COUNT) \
metamacro_at(COUNT, \
(id (*)(id, SEL)), \
(id (*)(id, SEL, id)), \
(id (*)(id, SEL, id, id)), \
(id (*)(id, SEL, id, id, id)), \
... // repeat until there are 19 id's
)
#define objc_call2(RECIEVER, SELECTOR, COUNT, ARR) \
(metamacro_cast(COUNT) objc_msgSend)(RECIEVER, SELECTOR \
metamacro_for_cxt(COUNT, CFIEXTRACTARGS,, ARR) \
)
这个宏的用法和你的完全一样,没有多余的参数:
- (void) doSomethingElseThree
{
NSArray *args = @[ @"three" ];
NSString *res = objc_call2(self, @selector(doSomething, 1, args);
NSLog(@"res = %@", res);
}
如果您希望支持多种返回类型,则需要多个 objc_call_returning_type 宏;或者写一个只接受返回类型的宏,不像我第一个接受整个 Actor 的宏,然后用 metamacros.h 尝试更多的宏体操。
关于ios - 在新的运行时使用 objc_msgSend 而不进行强制转换的替代方法,我们在Stack Overflow上找到一个类似的问题:
https://stackoverflow.com/questions/28612101/
|