1

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

我有一个使用 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'
4

2 回答 2

1

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

您应该检查的两个方面:

1)您是否将其“保存”CALayer在变量中而不保留它?如果您遇到任何自动释放池(包括在主线程上提供的那个),那么这些层可能会被无意释放。如评论中所述,由于这些不是自动发布的,因此可以在不打池的情况下发生这种情况。但是,当您持有参考时,它可能会在 CALayer 发布的任何时候发生。

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

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

于 2010-09-28T06:46:04.590 回答
0

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

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

- (CALayer *)hitTest:(CGPoint)thePoint

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

可用性 适用于 Mac OS X v10.5 及更高版本。在 CALayer.h 中声明

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

于 2011-10-16T13:09:31.933 回答