如何确定指针指向的值何时为nil

时间:2013-02-20 15:30:04

标签: ios objective-c cocoa-touch pointers cocos2d-iphone

我的情况是部队可以袭击建筑物。每个部队都有一个指向目标的指针。

@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而是我应该检查指针的值是零。

我尝试使用if (*_target),但遇到了消息

  

语句需要表达标量类型

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

4 个答案:

答案 0 :(得分:4)

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

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

更广泛地说,正如trojanfoe的回答所暗示的那样,你依赖于ARC将真实程序逻辑的弱引用行为归零。从理论上讲,这是可以的,因为(与他最初的陈述相反),ARC的归零弱行为是可靠的/确定性的。但是,它确实意味着你必须确保当目标不再在游戏领域(或其他任何方面)时总是解除分配。这有点脆弱。归零弱引用旨在避免保留周期(本质上是一种内存泄漏形式),而不是以您的方式实现逻辑的方式。 Trojanfoe解决方案的要点,您可以根据需要明确注册和取消注册目标,这可能是一个更强大的解决方案。

答案 1 :(得分:2)

我可能会忽略这些内容,但要检查target2属性是否为nil,请执行以下操作:

if ( self.target2 == nil ) {
     // Something
}

答案 2 :(得分: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;
}

答案 3 :(得分:1)

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

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

查找并删除该引用,这样可以正常工作。