我不知道权威,但是
__block
变量专门设计为能够超出其所在的范围,并包含跟踪其是堆栈备份还是堆备份的特殊运行时状态。例如:
#include <iostream>
#include <dispatch/dispatch.h>
using std::cerr; using std::endl;
struct destruct_logger
{
destruct_logger()
{}
destruct_logger(const destruct_logger& rhs)
{
cerr << "destruct_logger copy constructor: " << &rhs << " --> " << this << endl;
}
void dummy() {}
~destruct_logger()
{
cerr << "~destruct_logger on " << this << endl;
}
};
void my_function()
{
__block destruct_logger logger;
cerr << "Calling dispatch_after, &logger = " << &logger << endl;
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(),
^{
cerr << "Block firing\n";
logger.dummy();
});
cerr << "dispatch_after returned: &logger = " << &logger << endl;
}
int main(int argc, const char * argv[])
{
my_function();
cerr << "my_function() returned\n";
dispatch_main();
return 0;
}
如果运行该代码,将得到以下输出:
Calling dispatch_after, &logger = 0x7fff5fbff718
destruct_logger copy constructor: 0x7fff5fbff718 --> 0x100504700
dispatch_after returned: &logger = 0x100504700
~destruct_logger on 0x7fff5fbff718
my_function() returned
Block firing
~destruct_logger on 0x100504700
这里发生了很多事情:
-
在我们打电话之前
dispatch_after
,
logger
仍然基于堆栈。(0x7FFF地址)
-
调度后
内部执行
Block_copy()
捕获的块
记录器
. 这意味着现在必须将logger变量移到堆中。由于它是C++对象,这意味着调用复制构造函数。
-
事实上,之后
调度后
返回,
&logger
现在计算新(堆)地址。
-
当然,必须销毁原始堆栈实例。
-
只有在捕获块被销毁后,才会销毁堆实例。
所以A
亚块
“variable”实际上是一个更复杂的对象,可以在后台根据需要在内存中移动。
如果你随后回来
记录器
从
my_function
,rvo是不可能的,因为(a)它现在住在堆上,而不是堆栈上,(b)返回时不复制将允许由块捕获的实例发生变异。
我想可能会使它依赖于运行时状态-使用rvo内存进行堆栈备份,然后如果它被移到堆中,在函数返回时复制回返回值。但这会使在块上操作的函数复杂化,因为后备状态现在需要与变量分开存储。它似乎也过于复杂和令人惊讶的行为,所以我并不惊讶RVO没有发生在
亚块
变量。