我们可以在将 Objc 对象从 void* “桥接”到 UIKit 对象之前检查它是否被解除分配吗?

Can we check if an Objc object was deallocated before "bridging" it from void* to UIKit object?

提问人:olha 提问时间:5/25/2021 更新时间:5/26/2021 访问量:270

问:

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

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


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

// 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));

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

enter image description here

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

我见过 , , , 但没有看到像 .__bridge__bridge_transfer__bridge_retainedbridge_TRY

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

  1. 在这种情况下,我们能否以某种方式“安全地桥接”,并得到一个结果,如果触摸被解除分配。UITouch = nil
  2. 我们是否可以以某种方式订阅“UITouch”解除分配,以便我们在触摸对象被删除和指针无效之前删除相应的内容?UITouchCFDictionary
  3. 还有其他方法可以解决这个问题吗?
IOS Objective-C 内存管理 核心基础 UIWow

评论

1赞 EmilioPelaez 5/25/2021
该库创建于 9 年前,上次更新是在 5 年前。你的精力最好花在重新创建它所拥有的单个类上,而不是试图让它工作。
2赞 EmilioPelaez 5/25/2021
我越看我,情况就越糟。我不明白他们为什么要用 .CFDictionaryNSDictionary
0赞 Rob Napier 5/26/2021
@EmilioPelaez 评论很好地涵盖了它。NSDictionary 键需要符合 NSCopying(因为键被复制到字典中,而不是保留),而 UITouch 不符合 (并且不打算直接复制) 。
0赞 EmilioPelaez 5/26/2021
@RobNapier我想我错过了那个评论,但这在当时仍然是一个坏主意。 复制密钥以避免密钥不再可用的情况,这似乎是现在正在发生的事情。我支持我的第一条评论,这是一个非常过时的库,任何精力都花在重写它上,而不是试图弄清楚如何适应它。NSDictionary

答:

1赞 Володимир Ukraine 5/25/2021 #1

在第 83 行中,您使用 cycle 检查每个字典值,但在第 91 行中,您更改了字典大小。因此,最好在第 77 行添加其他,这将是相等的,并在第 91 行中删除数据,并在第 95 行中设置。foroutputDictionary_touchDictionaryoutputDictionary_touchDictionary = outputDictionary

这样会更安全

评论

0赞 olha 5/25/2021
谢谢!如果这消除了“__bridge”崩溃,我会尝试接受(我不确定这是崩溃的唯一原因)
2赞 Cy-4AH 5/26/2021 #2

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

1赞 Rob Napier 5/26/2021 #3

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

“非保留”CFDictionaries 的优点是,它们允许您存储对保留和释放没有响应的内容(例如,错位的内存)。但通常,您应该使用标准回调对它们启用正常的内存管理。取而代之的是:

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

用:

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

然后字典将为您保留其键和值。

在手势完成后,请小心按住 UITouch。文档禁止这样做

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

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

只是为了谈谈你的其他一些想法:

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

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

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

是的,但不要。:D您可以附加一个具有自己的关联对象,当它附加到的对象被释放时,它将触发,然后您可以执行响应操作。但这对于IMO这个问题来说太过分了。如果需要示例,请参阅 PMKVObserverDeallocSpydealloc

评论

0赞 olha 5/26/2021
谢谢你!从来没有听说过这个。关于触摸的保留,我会考虑的。associated object