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?
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.