views:

455

answers:

2

Hi,

I have the NSMutableArray *children in the datastructure-class "Foo" which is the superclass of many others, like "Bar1" and "Bar2". That array stores Bar1 and Bar2 objects to get a tree-like recursive parent-children-structure of subclasses from Foo. To access the objects in the array, I loop through them using the foreach loop in Objective-C:

for(Foo *aFoo in children) {
    ...
}

But often I only need to loop through the objects in the array that have a certain class, in this case I want to perform a task for each object of the class Bar1 in the array children. Using for(Bar1 *anObject in children) again loops through ALL objects and not only the ones with the class Bar1. Is there a way to achieve what I need?

+4  A: 

You have to loop over all objects and do a type check inside the loop.

for(id aFoo in children) {
    if ([aFoo isKindOfClass:[Bar1 class]])
        ...
    }
}
unbeli
this can be quite memory-expensive but apparently there's no other way. Thank You
Nils Fischer
no, it does not change your memory footprint, you are just reading what's already in memory
unbeli
In general, if you need to use `isKindOfClass:` to differentiate between classes in a collection, your design pattern is outside the norm. This is particularly true if your collection contains instances of classes that are entirely of your creation.
bbum
+2  A: 

You can do something like this:

NSPredicate* bar1Predicate = [NSPredicate predicateWithFormat:@"SELF.class == %@", [Bar1 class]];
NSArray* bar1z = [children filteredArrayUsingPredicate:bar1Predicate];
for(Bar1* bar in children) {
  // do something great
}

It's important to note, however, that this won't work with many standard Cocoa classes like NSString, NSNumber, etc. that use class clusters or special implementation classes (e.g., anything that is toll-free bridged with a CoreFoundation type) since the classes won't match exactly. However, this will work with classes you define as long as the class really is an instance of Bar1.

Emphasis Note: User @Alex suggested that it may not be clear that the classes must match exactly from my note above, so I am restating that. The classes must match exactly for this filter to work, so if you subclass Bar1 or provide some proxy class, you will have to adjust the filter in order for those classes to be included. As written, only instances of Bar1 will be returned in the filtered array.

Jason Coco
also, I think this is not available on the iPhone
unbeli
@unbeli: It is absolutely available on the iPhone, not that the OP mentioned the iPhone at all.
Jason Coco
But, if you ever subclass `Bar1`, this code will not match objects of the subclass. The OP doesn't say anything about subclasses, but it's important to note.
Alex
@Jason Coco right, it's available, no idea why I remembered it wasn't.
unbeli
@Alex: I was hoping that "since the classes won't match exactly" was enough, but I will edit it to add that more specifically as well.
Jason Coco
@unbeli: It was added with 3.0 when they added CoreData. If you haven't tried to use it since then, or didn't read the release notes when 3.0 came out, it would be easy to understand :)
Jason Coco