Objective c 没有匹配的函数用于调用。。。但只在一个街区内?

Objective c 没有匹配的函数用于调用。。。但只在一个街区内?,objective-c,closures,objective-c-blocks,Objective C,Closures,Objective C Blocks,我遇到了一个奇怪的情况。我在函数中有一些局部变量: JSContext *cx = ...; jsval successCb = ...; 有一个函数调用,它接受以下参数: //JS_RemoveValueRoot(JSContext *cx, jsval *vp); JS_RemoveValueRoot(cx, &successCb); //works 上面的汇编很好。但是,如果出现以下情况,则会出现编译时错误: id foo = ^() { JS_RemoveValueR

我遇到了一个奇怪的情况。我在函数中有一些局部变量:

JSContext *cx = ...;
jsval successCb = ...;
有一个函数调用,它接受以下参数:

//JS_RemoveValueRoot(JSContext *cx, jsval *vp);
JS_RemoveValueRoot(cx, &successCb); //works
上面的汇编很好。但是,如果出现以下情况,则会出现编译时错误:

id foo = ^() {
    JS_RemoveValueRoot(cx, &successCb);
}
从字面上说,如果我复制并粘贴行,如果它在它编译的块之外,但是如果它不是,它就不会。错误是:

No matching function for call to 'JS_RemoveValueRoot'
我怀疑在块闭包是如何实现的,但我对Objective C还不太熟悉,无法理解这一点。为什么会产生编译时错误?我该如何修复它

编辑:似乎如果我做了以下操作,我就不会再得到编译时错误,但这对我来说毫无意义,这总是一件坏事,所以我仍然希望得到一个解释

id foo = ^() {
    jsval localSuccessCb = successCb;
    JS_RemoveValueRoot(cx, &localSuccessCb);
};

啊我相信这就是问题所在。发件人:

这是第一个不同之处。逐块闭包中可用的变量类型为«const»。这意味着不能从块内部修改它们的值

因此,错误在于我传递的是
JS_RemoveValueRoot
a
const jsval*
,而不是
jsval*
。创建非常量的本地副本“解决”了问题(取决于该行为是否可接受,在本例中是可以接受的)

或者,我也可以将
jsval
声明为:

__block jsval successCb = ...;
在这种情况下,我不必创建本地非常量副本


在这种情况下,XCode确实提供了毫无帮助的错误消息…

更为复杂。是的,直接的问题是所有非
\u块
捕获的变量都是块内的
const
。因此,在块内
cx
具有type
JSContext*const
successCb
具有type
const jsval
。和
const jsval*
不能传递给
jsval*
。但是,您必须首先理解为什么变量是
const

块按创建时的值捕获非块变量。这意味着块内变量的副本和块外变量的副本是不同的独立变量,即使它们具有相同的名称。如果它不是
const
,您可能会试图在块内更改变量,并期望它在块外更改,但实际上并非如此。(当然,相反的问题仍然存在——您仍然可以在块外更改变量,因为它不是
常量
,并且想知道为什么它在块内不更改。)
\u块
通过使变量只有一个副本来解决此问题,在块的内部和外部之间共享的

然后,重要的是要考虑为什么
const
变量是不够的。如果您只需要变量的值,那么也可以使用
const
copy。当
const
不起作用时,通常是因为需要分配给变量。我们需要问,
JS_删除valueroot
需要一个指向变量的非
const
指针的原因是什么?是否要分配给变量?(如果是,我们是否关心块外的新值?因为如果不是,我们可以将
const
变量分配给块内的非
const
变量。)

事实证明它更复杂。根据
JS_Remove*Root
的文档,它既不使用指向的变量值,也不需要设置变量;相反,它需要变量的地址,这需要匹配传递给
JS_Add*Root
的地址。(实际上,我甚至不确定他们所做的事情是否需要一个
const
指针。)我假设
JS_AddValueRoot
是在包含块的函数体中、块外完成的。(我假设这是因为您说过
successCb
是一个局部变量,所以它必须在这个函数中;如果它在块中,那么它就没有意义了,因为
successCb
可能只是块的一个局部变量,因此不需要捕获。)

<>因为变量本身的地址很重要,让我们考虑在各种块变量捕获模式中会发生什么。非\u块变量现在显然不合适,因为内部和外部有两个单独的副本(因此有两个单独的地址)。因此,指定给
Add
Remove
的地址将不匹配。
\u块
变量是共享的,而且更好


但是,
\uuu块
变量仍然存在一个问题,可能会使其不匹配--
\uu块
变量的地址可能会随时间而改变!这涉及到如何实现块的细节。在当前的实现中,
\uu块
变量保存在一个特殊的结构(一种“对象”)中,该结构从堆栈开始,但是当任何捕获它的块被复制时,它会作为一个动态分配的结构“移动”到堆中。这非常类似于捕获变量的块对象如何从堆栈开始,但在被复制时移动到堆中。首先将其放在堆栈上是一种优化,并不保证会发生;但目前确实如此。
\u块
变量本身实际上是此结构内变量的访问,通过跟踪此结构所在位置的指针访问。当结构从堆栈移动到堆时,可以看到表达式
&successCb
的值发生了变化。(这在普通C中是不可能的。)因此,要获得匹配的地址,必须确保在将变量的地址传递到
Add
时已经发生移动。您可以通过强制复制捕获它的块来执行此操作。

您在哪里执行
JS\u AddValueRoot()