简体   繁体   中英

Converting a String to a “Method” - To be used in a Class Method call

I'm familiar with NSSelectorFromString function with which we can create a new SEL from a string.

The problem is that I cannot use it to perform a Class Method call since the performSelector method works only with instances as its receiver.

I need something that may function like this:

NSString* colorName = colorsArray[num];
NSString* methodName = [NSString stringWithFormat:@"%@Color", colorName]; //will create blueColor or redColor, etc...
SEL colorMethod = NSSelectorFromString(methodName);
self.view.backgroundColor = [UIColor performSelector:colorMethod]; //this is not valid... since NSObject only has performSelector as an Instance method...

Is there another way to hold an on-the-run variable representing a Method that can function as a Class Method and can be created from an NSString ?

Or a way to message a Class with a selector?

The problem is that I cannot use it to perform a Class Method call since the performSelector method works only with instances as its receiver.

This is wrong. What makes you think that the UIColor class object is not an "instance"? Class objects are objects, which means they are "instances" of some class. Class objects are instances of (some subclass of) their root class, which in the case of UIColor is NSObject . (In other words, the UIColor class objects is also an NSObject and supports all NSObject instance methods.)

If you want to understand how this works, every class object is an instance of a metaclass. Every class has its own metaclass, and metaclasses have inheritance following their classes (ie if A is superclass of B, then A's metaclass is superclass of B's metaclass). At the end, the metaclass of the root class inherits from the root class itself (so NSObject 's metaclass inherits from NSObject ). What this means is that class methods are inherited, and furthermore that the root class (in this case NSObject )'s instance methods are inherited as class methods by all classes with that root class.

just call performSelector on the class

#import <Foundation/Foundation.h>

@interface T : NSObject
+ (NSString*)foo;
+ (NSString*)redColor;
@end

@implementation T
+ (NSString*)foo {
    return @"bar";
}
+ (NSString*)redColor {
    return @"RED";
}
@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        id tclass = [T class];
        NSLog(@"%@", [tclass performSelector:@selector(foo)]);
        NSLog(@"%@", [tclass performSelector:@selector(redColor)]);

        SEL sel = NSSelectorFromString(@"redColor");
        NSLog(@"%@", [tclass performSelector:sel]);

    }
}

From Cocoa Fundamentals Guide (obsolete but this part is still valid):

The runtime system treats methods defined in the root class in a special way. Instance methods defined in a root class can be performed both by instances and by class objects. Therefore, all class objects have access to the instance methods defined in the root class.

The Foundation framework provides two root classes: NSObject and NSProxy. NSObject has a method documented as – performSelector: , but because it is a root class, any subclass is able to use it as a class method. For example: both +[UIColor performSelector:] and -[UIColor performSelector:] work. The explanation at a runtime level is in newacct's answer.

One calls the class method and the other calls the instance method. Example:

#import <Foundation/Foundation.h>

@interface A : NSObject
@end

@implementation A
+(void) x { NSLog(@"class method"); }
-(void) x { NSLog(@"instance method"); }
@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        [A performSelector:@selector(x)];
        [[A new] performSelector:@selector(x)];
    }
}

Prints

class method
instance method

You can also use NSInvocation:

SEL sel = NSSelectorFromString(@"whiteColor");
NSMethodSignature *sig = [UIColor methodSignatureForSelector:sel];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
[invocation setSelector:sel];
[invocation setTarget:[UIColor class]];
CFTypeRef retVal;
[invocation invoke];
[invocation getReturnValue:&retVal];
NSLog(@"retVal: %@", retVal);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM