Question

Animal is a class with a BOOL property called alive. Monkey, Zebra and Walrus are sub-classes of Animal. If I have an instance of an NSArray called zoo containing a mix of instances of Monkey, Zebra and Walrus, and I'd like to find the first alive Zebra instance, I might do something like this:

Zebra *zebra; 
for (Animal *animal in zoo) {
    if ([animal isMemberOfClass:[Zebra class]] && animal.alive) {
        zebra = animal;
        break;
    }
}

Problem is the compiler complains about incompatible pointer types when I set zebra = animal. If I do some casting like zebra = (Zebra *)animal then it seems to work, but I'm not sure that kind of casting is safe in Objective-C.

What is a better way of dealing with situations like this?

Was it helpful?

Solution

You're experiencing an "is a" problem.

If you had been assigning zebra to animal, there would have been no issue, since the class of zebra is Zebra, which "is a" Animal.

But with what you're doing, you're assigning animal to zebra, but animal is of class Animal, the superclass. The "is a" test fails, but a simple casting:

zebra = (Zebra *)animal;

takes care of the compiler warning. And yes, it's safe.

Here's a more modern way of doing the same thing. Incidentally, since, in the last line, -objectAtIndex: returns id, the aforementioned problem doesn't arise :

NSUInteger index = [zoo indexOfObjectPassingTest:^BOOL(Animal *animal, NSUInteger idx, BOOL *stop) {
    return ([animal isMemberOfClass:[Zebra class]] && animal.alive);
}];
Zebra *zebra = (index != NSNotFound) ? [zoo objectAtIndex:index] : nil;

Note that I did tweak one of the arguments to the block from id obj to Animal *animal; if you know your collection contains only references to instances of Animal, you certainly can do this.

OTHER TIPS

Thats definitely the way to do it in objective-C. If youre checking that its the kind of class your casting to first, its very safe. Even if its "typed" to something else, isMemberOfClass uses the runtime to report what it really is.

One more way to do same task.

[zoo enumerateObjectsusingBlock:^(id obj, NSUInteger index, BOOL *stop){
    if ([obj isMemberOfClass:[Zebra class] && animal.alive){
        zebra = (Zebra *)animal;
        *stop = YES;
    }
}];

BTW, this way of casting is safe.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top