Objective-C块和c++对象
Objective-C blocks and C++ objects
我有一个正在后台线程上执行的方法。从这个方法中,我试图在主线程上dispatch_async
一个块。该块使用一个局部c++对象,该对象应该是根据Apple引用复制构造的。我得到了一个分割错误,从跟踪中我看到一些非常粗略的事情正在发生。这是我的代码的简化版本。
struct A
{
A() { printf("0x%08x: A::A()n", this); }
A(A const &that) { printf("0x%08x: A::A(A const &%p)n", this, &that); }
~A() { printf("0x%08x: A::~A()n", this); }
void p() const { printf("0x%08x: A::p()n", this); }
};
- (void)runs_on_a_background_thread
{
A a;
a.p();
dispatch_async(dispatch_get_main_queue(), ^{
printf("block beginsn");
a.p();
printf("block endsn");
});
}
输出:
0xbfffc2af: A::A()
0xbfffc2af: A::p()
0xbfffc2a8: A::A(A const &0xbfffc2af)
0x057ae6b4: A::A(A const &0xbfffc2a8)
0xbfffc2a8: A::~A()
0xbfffc2af: A::~A()
0xbfffdfcf: A::A(A const &0x57ae6b4)
0xbfffdfcf: A::~A()
block begins
0xbfffdfcf: A::p()
block ends
0x057ae6b4: A::~A()
有两件事我不明白。第一个是为什么当它到达0xbfffdfcf: A::p()
时,该对象的析构函数已经被调用了。
第二件让我纠结的事是为什么会调用这么多复制构造函数。我期待一个。这应该发生在创建a
的副本以被块捕获时。
我使用Xcode 3.2.5与GCC。我在模拟器和设备上都遇到了相同的行为
我刚刚在LLVM 3.0上测试了这个。
0xb024ee18: A::A()
0xb024ee18: A::p()
0xb024ee04: A::A(A const &0xb024ee18)
0x06869364: A::A(A const &0xb024ee04)
0xb024ee04: A::~A()
0xb024ee18: A::~A()
block begins
0x06869364: A::p()
block ends
0x06869364: A::~A()
可以看到,在这种情况下,析构函数得到了适当的调用,我认为这是您使用的非常过时的编译器中的编译器错误。
本例中的副本似乎与我所期望的一致。当对象被捕获时,块将基于堆栈的对象复制到块中。然后,当块从堆栈复制到堆时,
我猜多个副本是由编译器隐式复制块几次引起的,虽然我不明白为什么需要复制块,你会认为一个实例可以在块发送到主线程时直接引用。
忽略多个副本,似乎块应该使用A的0x057ae6b4
实例,因为它在所有副本中幸存下来,并在块结束后被释放。听起来像是编译器的bug。
无论如何,您所做的都是极端反c++的,我建议您修改这段代码,使其具有更可预测的行为。我对你的代码的问题是,你在一个代码块中使用堆栈分配对象,该代码块将在未来的一些不确定的时间异步执行,在拥有该堆栈分配对象的函数结束后很久。为了支持这类事情,编译器必须在后台生成对象的副本,但是如果你看一下代码,没有迹象表明块内的a
是外部声明的a
的副本。如果你需要支持这种东西,我认为你最好把你的c++类转换成Objective-C,这样你就会有一个引用计数对象,它的行为方式更可预测。
如果这个对象需要留在c++域中,那么我建议您在堆上分配它并手动管理它的销毁,这是c++中堆分配对象的标准。例如,您可以这样做:
- (void)runs_on_a_background_thread
{
A* a = new A();
a->p();
dispatch_async(dispatch_get_main_queue(), ^{
printf("block beginsn");
a->p();
delete a;
printf("block endsn");
});
}
如果这对您来说听起来太原始,那么您可以看看是否使用auto_ptr
或更好的shared_ptr
更好。我怀疑这两个将有相同的编译器问题,当您使用堆栈上分配的A时,因为它们也将在堆栈上分配。但也许值得一试。
- 什么时候调用组成单元对象的析构函数
- 对RValue对象调用的LValue ref限定成员函数
- CMake-按正确顺序将项目与C运行时对象文件链接
- 空基优化子对象的地址
- 将对象数组的引用传递给函数
- 你能重载对象变量名本身返回的内容吗
- C++使用整数的压缩数组初始化对象
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 将对象移动到std::shared_ptr
- 代理对象的常量正确性
- 提升 ASIO 无法识别计时器对象
- 将Ref对象作为类成员
- 将包含C样式数组的对象初始化为成员变量(C++)
- 如何返回一个类的两个对象相加的结果
- 使用std::函数映射对象方法
- 是否需要删除包含对象的"pair"?
- 如何在自删除后将对象设置为nullptr
- 迭代时从向量和内存中删除对象
- 构造对象的歧义
- 使用"std::unordereded_map"映射到"std::list"对象