回到 sel_getUid() 的原始行为

Get back to sel_getUid()'s original behaviour

TL;DR:如何在没有实际注册的情况下检查具有给定名称的选择器是否已注册?

谢谢!


嗨,我有一个 Objective-C 应用程序和一堆 NSObjects,它们通过用 objc 编写的简单代理库导出到 Lua 状态。所有 Lua 端调用都像这样:

exported_objc_object:myMethodName(...)

-- same as --
exported_objc_object.myMethodName(exported_objc_object, ...)

-- same as --
key = 'myMethodName'
exported_objc_object[key](exported_objc_object, ...)

被转发,就像有人呼叫:

[objc_object lua_myMethodName:L];

// declared as
- (int)lua_myMethodName:(lua_State *)L { ... }

实际上,Lua-导出对象上的任何 'get' 操作码 returns 一个缓存的 Lua-闭包,在调用时将调用相应的 Objective-C 方法使用 sprintf(s, "lua_%s:", key)) && sel_getUid(s) 构造的选择器(包括所有检查)。如果生成的选择器没有根据 -[respondsToSelector:] 实现,那么 exported_objc_object.myMethodName 只是 returns nil.

显然,代理库必须通过 sel_getUid()sel_registerName() 进行动态查找(我相信 @selectorNSSelectorFromString() 也都在那里结束)。手册指出 sel_getUid() 旨在查找选择器名称(而不是立即将它们注册到 SEL 注册表中),但由于当时某人的 teh codez 中的错误,它的现代实现现在与 sel_registerName() 相同.

我可以坚持使用 sel_registerName() 行为,但这会留下消耗内存的攻击向量,因为某些恶意脚本可能会开始通过 sml object[makeRandomKey()] 在循环,从而永远溢出 SEL 注册表。如果 sel_getUid() 按计划工作,代理库将能够测试选择器的存在,然后才实际检查对象是否响应它,而无需过多注册。但事实并非如此。

这里有一个可能有用的 hack,它使用了选择器是 C 字符串这一依赖于实现的事实。

sel_isMapped((SEL)(void *)"lua_myMethodName:")