0

Say you have class A, which classes A1, A2, and A3 inherit from. All four classes implement a class method:

+ (NSString *)idString;

Now say you have an object

A *object1; //object may be of type A1, A2, or A3

Is there any way to call the appropriate idString method? I thought maybe:

[[object1 class] idString]

But that won't compile, says no know class method for selector idString. I also thought of treating idString like a private method and calling

[object1 idString]

But that says no interface declares the selector idString

Randall Schmidt
  • 93
  • 1
  • 12

4 Answers4

1

Getting the class object of the instance you have, [object class] and sending a message to that, is exactly how this is supposed to work. Classes are objects too in ObjC, and you can send the same message to different classes in exactly the same way you can send intValue to either an NSString or an NSNumber instance (or, init to (nearly) any instance!). This is fundamental to ObjC - the method is looked up based on the message at runtime. If this line:

[[object1 class] idString];

isn't compiling, then you have an error elsewhere. That's completely legal and the way you do what you're describing.

#import <Foundation/Foundation.h>

@interface Johnathan : NSObject
+ (NSString *)aNaughtyPhrase;
@end

@implementation Johnathan

+ (NSString *)aNaughtyPhrase
{
    return @"Knickers";
}

@end

@interface Johnny : Johnathan @end
@implementation Johnny

+ (NSString *)aNaughtyPhrase
{
    return @"Botty";
}

@end

@interface John : Johnathan @end
@implementation John

+ (NSString *)aNaughtyPhrase
{
    return @"Woo-woo";
}

@end

@interface Jack : Johnathan @end
@implementation Jack

+ (NSString *)aNaughtyPhrase
{
    return @"Semprini";
}

@end

int main(int argc, const char * argv[])
{

    @autoreleasepool {

        Johnathan * jt = [Johnathan new];
        Johnny * jy = [Johnny new];
        John * jn = [John new];
        Jack * jk = [Jack new];

        NSLog(@"%@", [[jt class] aNaughtyPhrase]);
        NSLog(@"%@", [[jy class] aNaughtyPhrase]);
        NSLog(@"%@", [[jn class] aNaughtyPhrase]);
        NSLog(@"%@", [[jk class] aNaughtyPhrase]);

    }
    return 0;
}
jscs
  • 63,694
  • 13
  • 151
  • 195
  • Thank you this made me look into the rest of the code and I did indeed find an inheritance problem. So after fixing that it does work as you described. – Randall Schmidt Aug 14 '12 at 19:03
  • I am glad to have helped and appreciate the award of the green checkmark, but it would be useful to future readers if, when you have a bit of time, you [post your own answer](http://meta.stackexchange.com/questions/17463/can-i-answer-my-own-questions-even-those-where-i-knew-the-answer-before-asking?lq=1) explaining what the true problem was and how you fixed it. – jscs Aug 14 '12 at 19:06
0
[(Class)[object1 class] idString]

is the way to go. If that doesn't compile, decrease the strictness of your compiler (probably exclude the -pedantic, -Werr, -Wall etc. flags).

  • Oh ok. Is there any way to perhaps cast [object1 class] to type [A class] explicitly? – Randall Schmidt Aug 14 '12 at 18:31
  • Thanks that will work but I am a little hesitant to decrease compiler strictness. So what would really be perfect for me would be something like [(Class A)[object1 class] idString]. But I am guessing there's no way to do something like that? [A class] returns an object of type Class. Can I make that more specific so that the compiler knows which class I'm invoking the class method on at compile time? – Randall Schmidt Aug 14 '12 at 18:40
  • @RandallSchmidt no. Classes are of type Class. If casting to (Class) works, that's enough. (And I really respect you not wanting to decrease the compiler strictness level!) –  Aug 14 '12 at 18:42
0

Just try with this,

if([object1 isKindOfClass:A])
{
  [A idString];
}
else if([object1 isKindOfClass:A1])
{
  [A1 idString];
}

This [object1 idString] gives you warning because you treated it as a private method. You can't call a private method by it's object, you need to call that method in it's implementation itself. Like [self idString]

Midhun MP
  • 103,496
  • 31
  • 153
  • 200
  • Futures releases will add many more subclasses of A and I am going for easy extensibility here so I don't want people to have to extend if/else statements everywhere when they add more classes. – Randall Schmidt Aug 14 '12 at 18:35
0

If you are trying to call a class method just call it by [Class method], this is because the method you are calling has no relation to the object itself, there's no relation between the instantiated object and the method that is being called. Hence it being a Class method.

8vius
  • 5,786
  • 14
  • 74
  • 136
  • If I have an object of type A2 and call [A idString] on it I will not get the right result. On that object I will want to call [A2 idString], but I do not know beforehand that the type of the object is A2 and as explained below I do not want to do a big if/else block. – Randall Schmidt Aug 14 '12 at 18:43
  • What I meant was that Class methods are to be called on the class, so making an object to then call the class method doesn't make much sense, instead just simply call the class method. Now, if it's a case that you receive an id type object in a method a wish to call a class method there the [[object class] classMethod] should work. – 8vius Aug 14 '12 at 18:52