Objective-C ARC和longjmp [英] Objective-C ARC and longjmp
问题描述
将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可能也有例外,但我不确定.
是否使用ARC并不重要;跳过系统框架中任何代码框架的任何setjmp
/longjmp
都将产生未定义的行为(出于同样的原因,不能将异常用于可恢复的错误处理).
所以,是的,该代码可能泄漏了. 可能,因为这取决于编译器在该块中以及在何处发出retain
/release
. 可能也是因为编译器是否发出retain
/release
会受到优化级别以及编译器版本的影响.
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
的自动化行为,则所有选择都将关闭.
What is the best practice for mixing Objective-C ARC with longjmp
?
I am using Lua as scripting language, and my platform exports custom library for scripts. Entry points do check arguments with luaL_checkinteger(L, 2)
(among others), which, in turn, may call luaL_typerror(L, 2, ...)
, that is implemented in Lua with setjmp
/longjmp
. As far as I know, ARC simply auto-generates retain
/release
code, but what happens if it longjmp
s out of scope? Will this code leak on mistyped arguments?
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;
}
In the snippet above, control
will be temporarily retained by ARC, but with longjmp
s uncatchable nature, corresponding release call may never happen. On the other hand, all arguments may be checked before assigning to control
variable.
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;
}
Does it resolve [potential] leak above? Are there better ways to do this?
UPDATE: longjmp
only unwinds to Lua internals, and never crosses any system code, except for Lua source (which is aware), and my entry points (which I hope are aware).
I'm pretty sure that second snippet does right, but I need kind of formal proof.
LATE UPDATE:
LuaJIT implements dwarf2-compatible errors, so they are just like C++ exceptions. Pass -fobjc-arc-exceptions
compiler flag to arc-enabled sources with Lua code and any retained object will be released on any lua_error
. Nothing to worry about now! You are still not allowed to throw errors across Cocoa runtime, though.
I recall that original Lua may be compiled with exceptions too, but I'm not sure.
Doesn't really matter if ARC is in use or not; any setjmp
/longjmp
that jumps over any frame of code from the system frameworks will yield undefined behavior (for the same reason that exceptions cannot be used for recoverable error handling).
So, yes, that code might leak. Might because it depends on whether the compiler emits a retain
/release
in that block and where. Might also because whether the compiler emits retain
/release
will be impacted by the optimization level and, over time, the version of the compiler.
longjmp
only unwinds to Lua internals, and never crosses any system code, except for Lua source (which is aware), and my entry points (which I hope are aware).
That is helpful. As long as you structure your entry points such that they never intermingle system scope with Lua jumpable scopes, you should be OK. I would recommend turning off ARC in the source files where you have to manage this (and, of course, put the ObjC->Lua interface into a nicely encapsulated bit of implementation so the rest of your code can be ARC clean).
Consider, though, that there is non-obvious risk:
for(id x in anArray) {
... lua call that causes longjmp ...
}
The above would cause lua to "jump over" system code. Same goes for enumerateWithBlock:
, KVO, Notification handlers, etc...
You're going to have to think very very carefully about every potential stack trigger by a call from Lua into your code. If that call triggers any kind of automated behavior on the part of the system that could then call Lua API that could trigger a longjmp
, all bets are off.
这篇关于Objective-C ARC和longjmp的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!