Nicolas Zozol
Nicolas Zozol

Reputation: 7048

Find the Selector of a Class method

I'm quite a newbie in Objective C, though I have some background in Java reflection.

Here, I have a classic class method findAll that find all the domain objects from the database. The class Univers directly inherits from DomainObject

@interface DomainObject : NSObject

  - (NSString *) execute : (NSString*) method  withJson:(NSString*)json;
  + (NSString*)findAll: (NSString*)json;

@end

@implementation DomainObject

  - (NSString *) execute: (NSString*) method  withJson:(NSString*)json{

      method = [NSString stringWithFormat:@"%@%@", method, @":"]; 
      //method is 'findAll:'
      NSString* result =  [ self performSelector: 
          NSSelectorFromString(method) withObject:json];// Error here
      return result;    
  }
@end

The code was working when findAll was NOT a class method (ie -findAll declaration), but now I have the error : NSInvalidArgumentException -[Univers findAll:] It clearly seems that the runtime is looking for an instance method.

Any idea to find my class method ?

Upvotes: 1

Views: 1153

Answers (1)

Regexident
Regexident

Reputation: 29562

Instead of calling

NSString* result =  [self performSelector:NSSelectorFromString(method) withObject:json];

you need to call

NSString* result =  [[self class] performSelector:NSSelectorFromString(method) withObject:json];

for class methods.

After all it's the object instance's class that supposed to be calling the method, not the instance itself.

Short explanation: NSObject implements - (Class)class; (not to be mistaken with + (Class)class of similar effect, which NSObject implements, too!) which returns the Class object of your instance object. Keep in mind that in Objective-C in addition to plain instance objects, Classes are actual objects, too: objects of type Class, that is (vs. id, NSObject, …).

See the documentation for the -class method here.


Btw, you should probably wrap your method call into an conditional block to prevent exceptions caused by calls to missing methods.

SEL selector = NSSelectorFromString(method);
if ([[self class] respondsToSelector:selector]) {
    NSString* result =  [[self class] performSelector:selector withObject:json];
}

In general it's a common pattern in Objective-C to call an object's class method by receiving the class object via [object class].

Consider this case of a class called Foo implementing a convenience method for returning an autporeleased instance of itself (to be called via: Foo *newFoo = [Foo foo];):

While it would certainly be possible to implement said method like this (after all we know the object's class name, right?):

+ (id)foo {
    return [[[Foo alloc] init] autorelease];
}

the correct way is this:

+ (id)foo {
    return [[[self alloc] init] autorelease];
}

As the first one would cause problems with polymorphism in subclasses (Such as a subclass called FooBar, for which it should clearly be [FooBar alloc] …, not [Foo alloc] …. Luckily [[self class] alloc] solves this dynamically). While this is clearly not the right place for a thorough explanation of this (rather offtopic one might say) it's certainly worth noting/warning about, imho.

Upvotes: 5

Related Questions