• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

ios - 在新的运行时使用 objc_msgSend 而不进行强制转换的替代方法

[复制链接]
菜鸟教程小白 发表于 2022-12-13 05:25:13 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题

我正在编写一个 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-推荐答案


使用 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/

回复

使用道具 举报

懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关注0

粉丝2

帖子830918

发布主题
阅读排行 更多
广告位

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap