views:

49

answers:

2

Ok, so I'm using Objective-C. Now, say I have:

TopClass : NSObject
- (int) getVal {return 1;}
MidClass : TopClass
- (int) getVal {return 2;}
BotClass : MidClass
- (int) getVal {return 3;}

I then put objects of each type into an NSMutableArray and take one out. What I want to do is run the getVal func on the appropriate object type, but when I put

id a = [allObjects objectAtIndex:0];
if ([a isKindOfClass:[TopClass class]]) 
{
    int i;
    i = [a getVal];
}

I get firstly a warning about multiple methods called getVal (presumably because the compiler can't determine the actual object type until runtime). But more seriously I also get an error "void value not ignored as it should be" and it won't compile.

If I don't try and use the return from [a getVal] then it compiles fine e.g.

[a getval];  //obviously no good if I want to use the return value

It will also work if I use isMemberOfClass statements to cast the object to a class before running the function e.g.

if ([a isMemberOfClass:[BotClass]) i = [(BotClass*) a getVal];

But surely I shouldn't have to do this to get the functionality I require? Otherwise I'll have to put in a statement for every single subclass, and worse have to add a new line if I add a new sub class, which rather defeats the point of method overriding doesn't it?

Surely there is a better way?

A: 

Since a BotClass is a MidClass and a MidClass is a TopClass, you could just set the type of a to TopClass*.

TopClass* a = [allObjects objectAtIndex:0];
if ([a isKindOfClass:[TopClass class]]) {
    int i;
    i = [a getVal];
}

The better way is to add -getVal to the @interface and #import it. Then the compiler will know such this method is likely to return an int and won't complain even if a is an id. Make sure the method name won't coincide with others, though.

(BTW, in ObjC, getters won't be named as -getFoo. The convention is just call it -foo.)

KennyTM
Perfect that makes total sense. I had almost been trying that, but casting from the array to a, so obviously running the method only used TopVal's method. This way I assume the compiler knows what to expect, but is not restricted by the cast into ignoring subclasses. Thanks very much
Rod
I'd say that first assigning to a TopClass variable and checking afterwards if the object really *is* of that kind is a funny idea. It will work as it's just a pointer (you could even use NSArray *a and it would compile with a warning but work in this case), but it's good hiding of code intention. :-)
Eiko
A: 

Your code compiles without any warning:

#import <Foundation/Foundation.h>


@interface TopClass : NSObject
{
}

- (int) getVal;

@end

@interface MidClass : TopClass
{
}

- (int) getVal;

@end

@interface BotClass : MidClass
{
}

- (int) getVal;

@end


@implementation TopClass

- (int) getVal {
    return 1;
}

@end

@implementation MidClass 

- (int) getVal
{
    return 2;
}

@end

@implementation BotClass

- (int) getVal
{
    return 3;
}

@end


int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    NSMutableArray *array = [NSMutableArray array];

    [array addObject:[[[TopClass alloc]init]autorelease]];
    [array addObject:[[[MidClass alloc]init]autorelease]];
    [array addObject:[[[BotClass alloc]init]autorelease]];

    for (int objectNumber = 0; objectNumber < [array count]; objectNumber++) {
        id a = [array objectAtIndex:objectNumber];
        if ([a isKindOfClass:[TopClass class]]) 
        {
            int i = [a getVal];
            NSLog(@"%d",i);
        }       
    }

    [pool drain];
    return 0;
}

The test "isKindOfClass" is always true in this case, as MidClass and BotClass inherit from TopClass.

Are you sure the compiler knows about the method signatures that you call?

Eiko