2

我有一种情况,部队可以攻击建筑物。每个部队都有一个指向其目标的指针。

@property (nonatomic, weak) Building *target;

在更新循环中,部队会定期对其目标造成伤害。

if (_target)  
{
    if (/*enough time has passed since last attack, attack again*/)
    {
        [_target attack];
        if (_target.health <= 0)
        {
            [_target removeFromParentAndCleanup:YES];   //Cocos2d
            _target = nil;
        }
    }
}
else /* Find new target */ 

问题是:

  • troop1处理掉下building1来并继续前进的打击building2
  • troop2正在攻击building1,但要等到它的下一次攻击才能确定building1是现在nil

我意识到问题在于troop2' 的指针尚未设置为 nil,而是我应该检查指针的值是否为 nil。

我尝试使用if (*_target)但收到消息

语句需要标量类型的表达式

如果有办法在 Objective-C 中实现这种比较?还有哪些其他选项可用于确定值何时发生变化?KVO?一些广泛的委托模式?

4

4 回答 4

4

当指针指向的对象被释放时,指针本身被设置为 nil。if (objectPointer == nil)始终是在 Objective-C/Cocoa 中检查对象是否为 nil 的方法。如果指针不为零,则表示该对象实际上并未被释放。如果取消引用指向对象的指针,则会得到一个结构,因此会出现编译器错误,即 if 表达式中需要一个标量值。

因此,在您的情况下,如果if(self.target != nil)没有给您期望的结果,您应该寻找对目标的剩余强引用(来自其他对象)。

更广泛地说,正如 trojanfoe 的回答所暗示的那样,您依赖 ARC 对真实程序逻辑的归零弱引用行为。理论上这是可以的,因为(与他最初的陈述相反),ARC 的归零弱行为是可靠的/确定的。但是,这确实意味着您必须确保当目标不再处于竞争环境(或其他任何情况)时,它们总是被解除分配。这有点脆弱。清零弱引用旨在避免保留循环(本质上是一种内存泄漏形式),而不是按照您正在做的方式实现逻辑的一种方式。trojanfoe 解决方案的要点,您可以根据需要显式注册和取消注册目标,这可能是一个更强大的解决方案。

于 2013-02-20T16:08:02.420 回答
2

我可能在这里忽略了一些东西,但是要检查target2属性是否为nil,只需执行以下操作:

if ( self.target2 == nil ) {
     // Something
}
于 2013-02-20T15:44:17.863 回答
1

我认为您过于依赖 ARC 的实现,因为您只知道如果指针是nil. 这是不可移植的,您可以在被释放的对象和指针变为 之间做出任何保证nil吗?

相反,使用对象的中央字典,映射到它们的唯一 ID 并只存储这个唯一 ID 而不是对象指针本身。在此示例中,我使用NSNumber递增整数作为键,但可能有更好的键可以使用。也是Object您要存储在此字典中的任何对象的基类:

// Probably ivars in a singleton class
unsigned _uniqueId = 1;
NSMutableDictionary *_objects;

- (NSNumber *)addObject:(Object *)object
{
    NSNumber *key = [NSNumber numberWithUnsignedInt:_uniqueId++];
    [_objects setObject:object forKey:key];
    return key;
}

- (void)removeObjectForKey:(NSNumber *)key
{
    [_objects removeObjectForKey:key];
}

- (Object *)getObjectForKey:(NSNumber *)key
{
    return [_objects objectForKey:key];
}

在您的目标中,只需存储构建密钥:

@property (strong) NSNumber *buildingKey;

并通过提供的方法获取建筑物:

Building *building = (Building *)[objectDictionary objectForKey:buildingKey];
if (building != nil)
{
    // building exists
}
else
{
    // building does not exist; throw away the key
    buildingKey = nil;
}
于 2013-02-20T15:55:05.080 回答
1

由于目标是弱引用,因此您的代码应该“按原样”工作,假设[_target removeFromParentAndCleanup:YES];删除了对目标的所有强引用。

当最后一个强引用被移除时,所有指向它的弱属性都会自动设置为nil. 如果它们没有自动设置为nil,那么在某处仍然存在对目标的强引用。

查找并删除该引用,这将正常工作。

于 2013-02-20T16:09:17.853 回答