Question

NSMutableArray *array = [[NSMutableArray alloc] init];
NSString *string = @"string";
[array addObject:string];
NSDate *date = [[NSDate alloc] init];
[array addObject:date];

    for (*placeholder* stuff in array)
        NSLog(@"one");

If I change placeholder to either NSString* or NSDate*, I expect to see "one", because the for loop should just ignore a non-matching type. However, the result is "one one".

Doesn't this imply that you should just have placeholder be id whatever the situation, since it doesn't seem to matter anyhow?

Was it helpful?

Solution

fast enumeration always iterates over all object in a collection. it does not filter.

The only thing that happens is, that you will have some strange casts.

if your array contains objects of differnt classes, you can determine the class for each object with isMemberOfClass:


if you would do for (NSDate *obj in array), any object in the array will be casts to NSDate, no matter if that is sense-full or not. and due to the nature of objective-c it will even work, as-long as you dont send a message that is only understandable by NSDate objects or send the object as an argument to a method that needs to receive a date object, as a cast does not change the object in anyway. A cast is just a promise you make to the compiler that you know what you are doing. Actually you also can call it a lie.


To answer your question title itself: You dont have to set the class inside the loop statement. the generic object type id is sufficient. But usually you have objects of one kind in an array — views, numbers, string, dates,…. by declaring the right class you gain some comfort like better autocompletion.

OTHER TIPS

Yes, using id (or some other common ancestor class) is the correct approach, and then it's necessary to determine which type of class has been enumerated in order to handle it differently:

for (id obj in array)
{
    if ([obj isMemberOfClass:[NSString class]])
    {
        NSString *str = (NSString *)obj;
        NSLog("obj is a string: %@", str);
    }
    else if ([obj isMemberOfClass:[NSDate class]])
    {
        NSDate *date = (NSDate *)obj;
        NSLog("obj is a date: %@", date);
    }
}

The problem has nothing to do with fast enumeration, but with collections which can contain any type of object. The same question arises when you access an individual element of an array:

id lastObject = [array lastObject];

or

NSString *string = [array lastObject];

Which will you chose? It all depends on your code. If you're sure that array only contains strings, then in my opinion it is better to use the second choice, because you get additional type checking, autocompletion, and method matching from the compiler (i.e. you won't get warnings if you call a method that has different signatures for two different objects). The same applies to fast enumeration: if your collection can contain any kind of object, use id. If you know what it contains, use the specific type. (And the same also applies to block tests. In NSArray's method

- (NSUInteger)indexOfObjectPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate

if you know it only contains strings for instance, you can replace id with NSString * in the block arguments. It won't change at all the compiled code or the behavior of your application, it will only change the compiler type checking.

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