Search code examples
objective-cclassvariablestypeofvariable-types

Get a reference to class object from uninitialized variable - not instantiated object


Let's say I have an uninitialized variable:

UIViewController *vc;

From this variable, I want to reference UIViewController, such that I could call alloc or new on it to return an instantiated object.

Essentially, I want to do:

UIViewController *vc = [*typeof(vc) new];

... which does not compile because the compiler expects an expression rather than a type.

If @encode returned the actual type, I could do something like:

UIViewController *vc = [NSClassFromString(@(@encode(*typeof(vc)))) new];

...however, @encode returns '@', which just means "generic object".

I realize it's a little philosophical in nature, but I get tired of typing and would like to make a macro like NEW(x). I also realize a similar macro could be made if it involves the actual declaration, but I am not satisfied with that syntax/angle.


Solution

  • Here's what I have... it doesn't seem ideal, since it makes a performance hit. Still looking for better answers.

    static Class classFromEncoding(const char *encoding) {
        char *className = strndup(encoding + 1, strchr(encoding, '=') - encoding - 1);
        Class retval = NSClassFromString(@(className));
        free(className);
        return retval;
    }
    
    #define NEW(variable) [classFromEncoding(@encode(typeof(*variable))) new]
    

    Here's a macro-only version:

    #define CLASS_FROM_VARIABLE(variable) \
    ^(const char *encoding) {             \
    char *className = strndup(encoding + 1, strchr(encoding, '=') - encoding - 1); \
    Class retval = NSClassFromString(@(className));      \
    free(className);                         \
    return retval;                              \
    }(@encode(typeof(*variable)))
    
    #define NEW(variable) [CLASS_FROM_VARIABLE(variable) new]
    #define ALLOC(variable) [CLASS_FROM_VARIABLE(variable) alloc]
    

    Variations can be made using objc_getClass() or NSString initWithBytes, perhaps with performance gains. Still, it's not a no-op, which is what I'd prefer.