我们可以在将 Objc 对象从 void*“桥接”到 UIKit 对象之前检查它是否已被释放?

问题描述

我正在使用 QTouchposeApplication 库(特别是来自 TRTouchposeApplication 分支的 develop 类)。

master 分支中的执行路径之一发生崩溃,作者在 develop 分支中修复了它们。但是,其中一个崩溃仍然存在。


假设我们有一个 CF-dictionary,它存储 UITouch 个对象而不保留它们:

// Dictionary of touches being displayed. Keys are UITouch pointers and values are UIView pointers that visually represent
// the touch on-screen. (A cfmutabledictionaryRef is used because NSDictionary requries its keys to conform to the
// NScopying protocol and UITouch doesn't. We don't need to retain either the UITouch or UIView instances because UITouch
// objects are persistent throughout a multi-touch sequence,and the UIViews are retained by their superview.)
cfmutabledictionaryRef _touchDictionary;

我们像这样将 UITouch 添加到字典中:

CFDictionarySetValue(_touchDictionary,(__bridge const void *)(touch),(__bridge const void *)(someUIView));

然后在某个时刻 UITouch 被系统释放(我在我的项目中使用 ARC)。 因此,当我们尝试“展开”触摸时,它会崩溃。我收到一条带有 Xcode Address Sanitizer 的消息 -[UITouch retain]: message sent to deallocated instance

enter image description here

所以崩溃的原因是我们将 ARC 对象存储为 void*,所以如果这个对象被释放,__bridge 就会崩溃。

我见过__bridge__bridge_transfer__bridge_retained,但没有看到像bridge_TRY这样的东西。

为了给这个崩溃写一个补丁,我想知道:

  1. 在这种情况下,我们能否以某种方式“安全地桥接”并获得结果 UITouch = nil,如果触摸被解除分配。
  2. 我们能否以某种方式订阅“UITouch”解除分配,以便我们从UITouch删除相应的CFDictionary 之前触摸对象将被删除并且指针无效?立>
  3. 还有其他方法可以解决这个问题吗?

解决方法

如果您需要在键或值处存储弱指针,则需要使用 NSMapTable 而不是字典

,

在第 83 行中,您使用循环 for 来检查每个字典值,但在第 91 行中您更改字典大小。所以最好在第 77 行添加其他 outputDictionary,这将等于 _touchDictionary,并在第 91 行从 outputDictionary 中删除数据,并在第 95 行将 _touchDictionary = outputDictionary 设置回来。

这样更安全

,

看这段代码,我相信字典应该保留触摸和视图。内存管理的简单规则是“保留你关心的,当你不再关心时释放”。很少有理由依赖其他对象为您保留内容。

“非保留”CFDictionaries 的优点是它们允许您存储对保留和释放不响应的内容(例如,分配的内存)。但通常,您应该使用标准回调对它们启用正常的内存管理。而不是这样:

_touchDictionary = CFDictionaryCreateMutable(NULL,10,NULL,NULL);

使用:

_touchDictionary = CFDictionaryCreateMutable(NULL,&kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);

然后字典会为你保留它的键和值。

在手势完成后,请务必小心按住 UITouch。 docs forbid this

触摸对象在多点触控序列中持续存在。您可以在处理多点触控序列时存储对触控的引用,只要在序列结束时释放该引用即可。 如果您需要在多点触控序列之外存储有关触控的信息,请从触控复制该信息。

但我认为您不一定做错了。崩溃发生在您移除触摸的点上,我认为这是在序列的末尾。

只是谈谈您的一些其他想法:

在这种情况下,我们能否以某种方式“安全地桥接”并得到结果 UITouch = nil,如果触摸被释放。

是的,但这需要弱指针,这可能是一个太大的变化。 Cy-4AH 的 NSMapTable 建议可以做到这一点,但您需要在整个代码中进行更改。

我们可以以某种方式订阅“UITouch”释放

是的,但不要。 :D 你可以附加一个 associated object ,它有自己的 dealloc,当它所附加的对象被释放时它会触发,然后你可以做一些响应。但这对于这个问题 IMO 来说太过分了。如果您需要示例,请参阅 PMKVObserverDeallocSpy

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...