在 CALayer -hitTest 中崩溃:

Posted

技术标签:

【中文标题】在 CALayer -hitTest 中崩溃:【英文标题】:Crash while in CALayer -hitTest: 【发布时间】:2010-09-28 06:19:38 【问题描述】:

这个问题真的把我难住了……

我有一个使用 UIPanGestureRecognizer 的 iPad 项目,并且在我的 handlePanGesture 中使用以下方法调用:

- (AIEnemyUnit *) hitTestForEnemyUnit:(CGPoint)where 
    CALayer * layer = [self hitTest:where];

    while (layer) 
        if ([layer isKindOfClass:[AIEnemyUnit class]]) 
            return (AIEnemyUnit *)layer;
         else 
            layer = layer.superlayer;
        
    

    return nil;

一旦我“找到”了一个 AIEnemyUnit 层,我继续拖动,一切正常。除了大约在第 6 到第 10 次“拖动”时,调试器仅在 CALayer -hitTest 深处发生崩溃:

modifying layer that is being finalized - 0x124530
*** -[NSCFSet hitTest:]: unrecognized selector sent to instance 0x124530
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', 
reason: 
'*** -[NSCFSet hitTest:]: unrecognized selector sent to instance 0x124530'

【问题讨论】:

【参考方案1】:

从症状来看,您似乎过度释放了 CALayer。

您应该检查的两个方面:

1) 您是否将这个CALayer“保存”在变量中而不保留它?如果您遇到任何自动释放池(包括主线程上提供的那个),那么这些层可能会被无意释放。正如在 cmets 中所指出的,由于这些不是自动释放的,因此可以在不碰到池的情况下发生这种情况。但是,当您持有引用时,只要释放 CALayer 就可能发生这种情况。

2) 稍后您将在此层上明确调用 release。由于您按原样获得此层(hitTest:superlayer 都返回没有额外保留计数的对象)您没有所有权,因此不应释放它。

另一个有用的调试工具是使用NSZombies,以及链接到那里的其他一些技术。 NSZombies 基本上允许您的应用程序在您访问已发布的对象时崩溃,这有望为您提供更有意义的堆栈跟踪。

【讨论】:

好答案。一件事:hitTest:superlayer 返回的层不会自动释放,但它们也不会保留。这可能会导致崩溃:CALayer *sublayer = [[layer sublayers] objectAtIndex:0]; [sublayer removeFromSuperlayer]; [sublayer superlayer]; 谢谢,我不太确定,但知道它们不属于调用者。修好了。 感谢您的修复!我已经从“@synthesize dragItem;”切换过来了。到手动设置器并忘记设置'dragItem = [newDragItem retain];'。我在其他帖子中看到“正在完成的修改层”消息与保留计数问题有关,但我的错误消息完全不同,我不确定这是否是答案。但由于其他帖子,我仔细查看了我的代码,看看我调用了多少次 -release 和 -removeFromSuperlayer。我非常专注于发布方面,我没有考虑保留,所以谢谢【参考方案2】:

我认为 hitTest 文档中实际上存在一些“错误信息”。我自己将 4 个子类视图的实例放到窗口上时遇到了类似的问题,每个实例中有四个子层。 4 个视图子类中的每一个都定义了一个 touchesBegan:withEvent 方法和一个 touchesEnded:withEvent 方法。我发现如果我的触摸落在或结束于最左上角的视图中,我的 hitTest 返回了一个有效的子层。但是,其他三个视图中的任何一个中的 hitTests 都为子层返回 nil。像你一样,我完全被难住了,直到我决定用窗口坐标系中的一个触摸点替换视图坐标系中的触摸点,然后一切正常。我复制了 hitTest 方法的文档:

命中测试: 返回包含指定点的层层次结构(包括其自身)中接收器的最远后代。

- (CALayer *)hitTest:(CGPoint)thePoint

参数 点 接收者超层坐标系中的一个点。 返回值 包含 thePoint 的层,如果该点位于接收器的边界矩形之外,则为 nil。

可用性 适用于 Mac OS X v10.5 及更高版本。 宣布于 CALayer.h

根据我的观察,我认为对“thePoint”的解释是错误的。我认为它应该是“包含接收器的窗口坐标系中的一个点”。我认为左上角视图给出有效 hitTests 的唯一原因是触摸的坐标 - 在那个位置 - 与窗口中的触摸坐标相同。不知道这是否对您有帮助,但它帮助我使我的逻辑正常工作。 V.V.

【讨论】:

以上是关于在 CALayer -hitTest 中崩溃:的主要内容,如果未能解决你的问题,请参考以下文章

什么是 CALayer 和 CAGradientLayer?

CALayer初认识

随机“CALayerInvalidGeometry原因:CALayer位置包含NaN”异常

如何将 UIView 添加到 CALayer

-[CALayer retain]:消息发送到释放的实例

vImageAlphaBlend 崩溃