2

将 Objective-C ARC 与 混合的最佳实践是什么longjmp

我使用 Lua 作为脚本语言,我的平台为脚本导出自定义库。入口点确实使用luaL_checkinteger(L, 2)(以及其他)检查参数,这反过来可能会调用luaL_typerror(L, 2, ...),这是在 Lua 中使用setjmp/实现的longjmp。据我所知,ARC 只是自动生成retain/release代码,但是如果longjmp超出范围会发生什么?此代码是否会因输入错误的参数而泄漏?

static int
set_tag(lua_State *L)
{
    NSControl *control = (__bridge NSControl *)lua_topointer(L, 1);
    [control setTag:(NSInteger)luaL_checkinteger(L, 2)]; // may longjmp!
    return 0;
}

在上面的代码片段中,control将被 ARC 临时保留,但具有longjmp不可捕获的性质,相应的释放调用可能永远不会发生。另一方面,可以在分配给变量之前检查所有参数。control

static int
set_tag(lua_State *L)
{
    NSInteger tag = luaL_checkinteger(L, 2); // may longjmp!
    NSControl *control = (__bridge NSControl *)lua_topointer(L, 1);
    [control setTag:tag];
    return 0;
}

它是否解决了上面的[潜在]泄漏?有没有更好的方法来做到这一点?

更新:longjmp只展开到 Lua 内部,从不跨越任何系统代码,除了 Lua 源(知道)和我的入口点(我希望知道)。

我很确定第二个片段是正确的,但我需要一种正式的证明。


最新更新:

LuaJIT 实现了与 dwarf2 兼容的错误,因此它们就像 C++ 异常一样。使用 Lua 代码将编译器标志传递-fobjc-arc-exceptions给启用 arc 的源,并且任何保留的对象都将在任何lua_error. 现在没什么好担心的!不过,您仍然不允许在 Cocoa 运行时抛出错误。

我记得原来的 Lua 也可能编译时有异常,但我不确定。

4

2 回答 2

1

longjmp()可能会导致 ARC 崩溃或泄漏。安排代码以便longjmp()ARC 不会干扰是很困难的。

如果longjmp()仅用于致命错误路径,并且您希望停止该过程作为响应,那么您可以忽略该问题。这就是 ARC 默认对 C++/ObjC 异常所做的事情。抛出异常时,ARC 代码预计会泄漏。有一个编译器选项可以为异常启用清理代码,但这会损害性能。

如果这不是进程终止错误,那么您最好的选择是在任何可能被调用longjmp()跳过的代码中关闭 ARC 。longjmp()

于 2013-12-23T00:41:50.623 回答
1

ARC是否在使用并不重要;任何从系统框架跳过任何代码帧的setjmp/longjmp都会产生未定义的行为(出于同样的原因,异常不能用于可恢复的错误处理)。

所以,是的,该代码可能会泄漏。 可能是因为它取决于编译器是否在该块中 发出retain/以及在哪里发出。也可能是因为编译器是否发出/会受到优化级别以及随着时间的推移编译器版本的影响。releaseretainrelease

longjmp只展开到 Lua 内部,并且从不跨越任何系统代码,除了 Lua 源(知道)和我的入口点(我希望知道)。

这很有帮助。只要你构建你的入口点,使它们永远不会将系统范围与 Lua 可跳转范围混合,你应该没问题。我建议在你必须管理它的源文件中关闭 ARC(当然,将 ObjC->Lua 接口放入一个很好封装的实现中,这样你的其余代码就可以是 ARC 干净的)。

但是,请考虑存在不明显的风险:

for(id x in anArray) {
    ... lua call that causes longjmp ...
}

以上将导致lua“跳过”系统代码。同样适用于enumerateWithBlock:,KVO,通知处理程序等......

您将不得不非常仔细地考虑通过从 Lua 调用代码来触发的每个潜在堆栈触发器。如果该调用触发了系统部分的任何类型的自动化行为,然后可以调用可能触发的 Lua API,那么longjmp所有的赌注都没有了。

于 2013-12-21T17:06:39.603 回答