当没有其他引用保留对象时,如何将 id 对象安全地存储在 ARC 下的 C++ void* 成员中?
Posted
技术标签:
【中文标题】当没有其他引用保留对象时,如何将 id 对象安全地存储在 ARC 下的 C++ void* 成员中?【英文标题】:How to safely store an id object in a C++ void* member under ARC when no other references hold on to the object? 【发布时间】:2013-01-24 19:10:22 【问题描述】:我正在使用 Box2D (C++),我创建了一个 Objective-C 对象并将其分配给 Box2D 主体的 userData
属性,该属性的类型为 void*
。
现在,在某些情况下,void* userData
可能是对该 ObjC 对象的唯一活动引用。因此,因为我在作业中使用了(__bridge void*)
,所以 ARC 放手了。这是我需要解决的问题。
我一直在思考如何防止这种情况发生?我阅读了Clang's ARC documentation,特别是关于桥梁铸造的部分(以及关于 SO 的问答)以及对他们认为“不正确”的各种桥梁铸造结构点头。
不过,我的第一个想法是在对 userData 的初始分配中使用 (__bridge_retained void*)
。但这让我想知道如何平衡保留?我显然无法向对象发送释放。
所以我必须CFRelease()
对象吗?还是需要CFBridgingRelease()
?还是在这里都是非法的?
(__bridge_transfer void*)
从userData
转换为临时 id 类型是否足够了,也许在之后将 userData 设置为 NULL?这是个好主意吗?
我知道另一种方法是为userData
对象保留一个单独的NSArray
/NSDictionary
,并使它们与 Box2D 主体的生命周期保持同步,并与它们的 Box2D 主体同步添加和删除它们。
但这感觉有点矫枉过正,因为在这里我知道自己在做什么,我知道只要 Box2D 主体处于活动状态,我就需要 +1
对象,而当 Box2D 主体被移除时,我需要 -1
对象.另外,我知道只有两种方法可以添加和删除 Box2D 主体,并且在我的框架中甚至无法直接访问 userData
,因为所有 Box2D 对象都隐藏在 Objective-C 接口/包装器后面。
暂时搁置可能“格式不正确”,您建议我在这种情况下应该怎么做?
【问题讨论】:
@Emil:感谢修复内联代码,我正打算自己做。 没问题,我必须承认在一个 20k 用户的帖子中编辑如此琐碎的事情很奇怪!哈:D 是的,有时我会问一些非常愚蠢的问题:) 【参考方案1】:__bridge_retained
表示“保留此 ARC 对象,将其发送到非 ARC 区域”。当您需要创建一个“未跟踪的”void *
时,您可以调用它。所以,就你而言,userData = (__bridge_retained void *)obj
。
__bridge_transfer
的意思是“释放这个物体,把它从非 ARC 的土地上拉回来”。当您想要有效地使void *
无效时,您可以调用它。所以,obj = (__bridge_transfer id)userData
。在此之后,userData
指针不能安全使用;相反,您只使用obj
。当obj
超出范围时,ARC 将最后一次释放它。这可能需要专门为此目的创建一个临时的id
。
因此,在您的情况下,您确实希望在将对象发送到 Box2D 时使用 __bridge_retained
,并在您希望使 userData
无效时使用 __bridge_transfer
。如果您需要以 Objective-C 对象的形式访问 userData
,但不使指针无效,请使用普通的 __bridge
。
【讨论】:
好。我主要对将它的回退感到困惑,因为它似乎没有对对象“做任何事情”,而实际上它确实如此。风格方面,我想我会更喜欢:CFBridgingRelease(body->GetUserData()),然后是 body->SetUserData(NULL)。据我了解,这与 __bridge_transfer 演员表相同。 我自己实际上会使用__bridge_*
,因为这与CF没有任何关系。
嗯……是的,但是我需要分配给本地 var,然后还要对该 var 做一些事情(将其设置为 nil),这样编译器就不会抱怨未使用的 var。
有趣,CFBridgingRelease 只是做了一个__bridge_transfer,它只是一个声明为的宏: NS_INLINE id CFBridgingRelease(CFTypeRef CF_CONSUMED X) return (__bridge_transfer id)X; (如果我忽略返回值,编译器不会抱怨未使用的结果)【参考方案2】:
您误解了文档作者对“格式错误”的含义。这是格式错误的:
NSData* data; // Initialized
NSData* data2= (__bridge NSData*) data;
这也是格式错误的:
void* data; // Initialized
void* data2= (__bridge void*) data;
这不是格式错误:
NSData* data; // Initialized
void* data2= (__bridge void*) data;
不病态就足够了,左值是可保留的,右值是不可保留的,反之亦然。因此,由于在您的情况下您将对象指针转换为原始指针,反之亦然,您的方法是正确的。
在您的位置,我将实现一个智能指针,该指针在构造时发送 CFBridgingRetain 消息,在销毁时发送 CFBridgingRelease 消息。
【讨论】:
以上是关于当没有其他引用保留对象时,如何将 id 对象安全地存储在 ARC 下的 C++ void* 成员中?的主要内容,如果未能解决你的问题,请参考以下文章