1

我在 Objective-C 中有一行非常简单的代码:

if ((selectedEntity != nil) && [selectedEntity isKindOfClass:[MobileEntity class]])

有时,我无法判断,游戏在这行代码上崩溃,并带有 EXC-BAD-ACCESS。通常似乎是从比赛场地中移除某些东西的时间,所以我猜测selectedEntity是什么被释放了,然后就是这个结果。除了不可能选择退出实体(但谁知道,也许这在我的代码中实际上并非如此......),事实上我在访问之前专门检查是否存在selectedEntity意味着我应该这里没有任何问题。Objective-C 应该支持布尔短路,但它似乎不是编辑:看起来短路与问题无关。

另外,我在这个代码块周围放了一个@try/@catch,因为我知道它每隔一段时间就会爆炸,但这似乎被忽略了(我猜 EXC-BAD-ACCESS 无法被捕获)。

所以基本上我想知道是否有人知道我可以抓住它并把它扔掉(因为我不在乎这个错误,只要它不会导致游戏崩溃)或者可以解释为什么它可能会发生. 我知道 Objective-C 用“nil”值做了一些奇怪的事情,所以我猜它指向一些既不是对象指针也不是 nil 的奇怪空间。

编辑:澄清一下,我知道下面的代码是错误的,这就是我猜想在我的程序中发生的事情。我在问这是否会导致问题——确实如此。:-)

编辑:看起来有一个边缘案例允许您在实体被删除之前选择它。所以,看起来代码的进展是这样的:

selectedEntity = obj;
NSAutoreleasePool *pool = ...;
[obj release];
if (selectedEntity != nil && etc...) {}
[pool release];

所以我猜是因为 Autorelease 池还没有被释放,所以对象不是 nil,但它的保留计数是 0,所以无论如何都不允许访问它......或者类似的东西?

另外,我的游戏是单线程的,所以这不是线程问题。

编辑:我通过两种方式解决了这个问题。首先,我不允许在那个边缘案例中选择实体。其次,我不只是调用 [entities removeObjectAtIndex:i] (删除将被删除的任何实体的代码),而是将其更改为:

//Deselect it if it has been selected.
if (entity == selectedEntity)
{
    selectedEntity = nil;
}

[entities removeObjectAtIndex:i];

只需确保在释放变量的同时将 nil 分配给变量,正如 jib 建议的那样。

4

5 回答 5

9

这与短路无关。Objective-C 将消息吃到 nil,因此selectedEntity != nil不需要检查(因为对于 BOOL 返回类型,messages-to-nil 将返回 NO)。

EXC_BAD_ACCESS 不是可捕获的异常。这是一个灾难性的失败,通常是由于试图跟随无效的指针而引起的。

很可能,无论 selectedEntity 指向的对象在代码执行之前已被释放。因此,它既不是 nil 也不是有效对象。

打开 NSZombies 并重试。

如果您的应用程序是线程化的,您是否正确地跨线程同步 selectedEntity(请记住,通常不支持从辅助线程欺骗 UI)?


您的帖子已被编辑以表明修复是:

//Deselect it if it has been selected.
if (entity == selectedEntity)
{
    selectedEntity = nil;
}

[entities removeObjectAtIndex:i];

这解决了这个问题,因为 NSMutableArray 将在删除时释放对象。如果保留计数降为零,则对象被解除分配,并且 selectedEntity 将指向一个被解除分配的对象。

于 2009-08-31T19:45:27.540 回答
6

如果一个对象 (selectedEntity) 已被释放并解除分配,则它不是 == nil。它是一个指向任意一块内存的指针,并且推迟它( if(selectedEntity!=nil ) 是一个编程错误 (EXC_BAD_ACCESS)。

因此,常见的 obj-c 范式:-

[选定实体发布];selectedEntity = nil;

于 2009-08-31T20:11:35.770 回答
0
 

selectedEntity = obj;
NSAutoreleasePool *pool = ...;
[obj release];
if (selectedEntity != nil && etc...) {}
[pool release];

 

你在这里有一个悬空指针或僵尸。selectedEntity 指向 obj,它会在您引用 selectedEntity 之前立即发布。这使得 selectedEntity 不是 nil 而是无效的对象,因此任何取消引用都会崩溃。

您想自动释放 obj 而不是释放它。

于 2009-08-31T20:15:34.343 回答
0

我刚刚读了这个http://developer.apple.com/mac/library/qa/qa2004/qa1367.html这表明你得到的错误是过度释放对象的结果。这意味着尽管 selectedEntity 是 nill,但您已多次发布它,但它不再属于您的使用了..

于 2009-08-31T19:47:22.577 回答
0

在 OBJC_EXCEPTION_THROW 上放置一个断点并查看它真正被抛出的位置。该行不应该自己抛出 EXC_BAD_ACCESS。

您是否可能在 IF 块中做一些可能导致异常的事情?

于 2009-08-31T19:53:48.960 回答