Take the 2-minute tour ×

I have a situation where troops can attack buildings. Each troop keeps a pointer to its target.

@property (nonatomic, weak) Building *target;

In an update loop, the troops periodically cause damage to their 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 */ 

The problem is:

  • troop1 deals the blow that fells building1 and moves on to building2
  • troop2 was attacking building1 but waits until its next attack to determine that building1 is now nil.

I realise the problem is that troop2's pointer has not been set to nil and instead I should be checking that the value of the pointer is nil.

I tried using if (*_target) but was met with the message

Statement requires expression of scalar type

If there a way to achieve this kind of comparison in Objective-C? What other options are there for determining when a value has changed? KVO? Some extensive delegate pattern?

share|improve this question
_target is already a pointer so if (_target) or if (_target != nil) is correct. –  trojanfoe Feb 20 '13 at 15:34
Can you elaborate on how target1 and target2 are defined? Are they both defined according to the pattern at the beginning of your question? –  bdesham Feb 20 '13 at 15:34
It's not 100% clear what you're asking, but pointers to Objective-C are essentially never dereferenced like that, and weak object pointers are indeed nil'd when the object in question is deallocated. In other words, the cause of the problem is not as you've described. –  Andrew Madsen Feb 20 '13 at 15:36
@bdesham, Yes, both are defined in the same way and would be assigned to that property. –  James Webster Feb 20 '13 at 15:36
@trojanfoe You should turn your comment into an answer. –  bdesham Feb 20 '13 at 15:37
show 9 more comments

4 Answers

up vote 4 down vote accepted

It is the pointer itself that is set to nil when the object it points to is deallocated. if (objectPointer == nil) is always the way to check if an object is nil in Objective-C/Cocoa. If the pointer is not nil, it means the object in question has not in fact been deallocated. If you dereference a pointer to an object, you get a struct, hence the compiler error about needing a scalar value in the if expression.

So, in your case, if if(self.target != nil) is not giving you the result you expect, you should look for remaining strong references to the target (from other objects).

More broadly, as hinted at by trojanfoe's answer, you're relying on ARC's zeroing weak reference behavior for real program logic. In theory this is OK, as (contrary to his initial statement), ARC's zeroing weak behavior is reliable/deterministic. But, it does mean that you have to ensure that targets are always deallocated when they're no longer on the playing field (or whatever). This is a bit fragile. Zeroing weak references are intended as a way to avoid retain cycles (essentially a form of memory leak), rather than as a way to implement logic the way you're doing. The gist of trojanfoe's solution, where you explicitly register and unregister targets as necessary, is probably a more robust solution.

share|improve this answer
add comment

There may be something that I have overlooked here, but to check if the target2 property is nil, just do:

if ( self.target2 == nil ) {
     // Something
share|improve this answer
There is no target2 property, the target2 in my example is another instance of Troop. This is my fault, badly named! –  James Webster Feb 20 '13 at 15:45
I've amended the question –  James Webster Feb 20 '13 at 15:47
That's what the if (_target) attempts to do, but it returns a non-nil value –  James Webster Feb 20 '13 at 15:49
The problem is if (_target) checks the pointer, I need to check the value of the pointer –  James Webster Feb 20 '13 at 15:50
Yeah, that was part of my question: If there a way to achieve this kind of comparison in Objective-C? –  James Webster Feb 20 '13 at 16:03
show 5 more comments

I think you are relying too heavily on the implementation of ARC in that you only know if an object has been removed if the pointer is nil. This is non-portable and can you make any guarantee between the object being released and the pointer becoming nil?

Instead, use a central dictionary of objects, mapped against their unique ID and store just this unique ID rather than the object pointer itself. In this example I'm using a NSNumber for the key using an incrementing integer, but there are probably better keys that can be used. Also Object is the base class of any object you want to store in this dictionary:

// 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];

And in your target, simply store the building key:

@property (strong) NSNumber *buildingKey;

and get the building via the methods provided:

Building *building = (Building *)[objectDictionary objectForKey:buildingKey];
if (building != nil)
    // building exists
    // building does not exist; throw away the key
    buildingKey = nil;
share|improve this answer
"This is non-portable and can you make any guarantee between the object being released and the pointer becoming nil?" True, this is non-portable to another compiler that doesn't support ARC. However, YES, you can guarantee that when an object is released that the pointer will become nil because that is what a weak reference in ARC does. It is part of the definition of weak references. –  lnafziger Feb 20 '13 at 15:58
@lnafziger OK, that's good to know. –  trojanfoe Feb 20 '13 at 15:59
I was reading this slowly before I disputed, but Inafziger has done it for me =] –  James Webster Feb 20 '13 at 16:00
Quite an overkill solution, I just added an isDestroyed Boolean property to Building =P –  James Webster Feb 20 '13 at 16:18
add comment

Since target is a weak reference, your code should work "as-is", assuming that [_target removeFromParentAndCleanup:YES]; removes all strong references to the target.

When the last strong reference is removed, all of the weak properties pointing to it will automatically be set to nil. If they are not automatically set to nil, then there is still a strong reference to the target somewhere.

Find and remove that reference, and this will work fine.

share|improve this answer
Note also, that unless you have a specific reason to use the iVar directly, it is common to use the method (ie self.target) so that it is easier in the future if you suddenly find that you need to make changes to the logic that provides a target. –  lnafziger Feb 20 '13 at 16:13
add comment

Your Answer


