objective-ccocoaobjective-c-runtimecydia-substratesimbl

Subclass Objective-C class without linking with the superclass?


I'm writing a SIMBL plugin for Spotlight, and I'm trying to create a subclass of an internal Spotlight type. While I can get headers directly from the executable using class-dump, I don't have a static library to link against, so compiling a subclass of one of those internal classes fails (even though the classes will be available at runtime). I've been following Mike Ash's instructions on subclassing classes at runtime, but it's pretty inconvenient. Is there any way to create a subclass of an Objective-C class without the superclass being available at link time?


Solution

  • This is entirely possible, and not actually very difficult to do. Here's a simple example with NSValue:

    @interface MySubclass : NSObject
    
    -(void) someMethod;
    
    @end
    
    @implementation MySubclass
    
    +(void) load {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wdeprecated"
    
        class_setSuperclass(self, NSClassFromString(@"NSValue"));
    
    #pragma clang diagnostic pop
    }
    
    -(void) someMethod {
        NSLog(@"%@", [self superclass]);
    }
    
    -(const char *) objCType {
        return @encode(int);
    }
    
    -(void) getValue:(void *)value {
        if (value) {
            *((int *) value) = 10;
        }
    }
    
    @end
    
    int main() {
        MySubclass *theSubclass = [MySubclass new];
        [theSubclass someMethod];
    
        NSLog(@"%i", [theSubclass isKindOfClass:[NSValue class]]);
    }
    

    class_setSuperclass, while deprecated, still has an implementation as of OS X 10.10, and can be done after the class has been registered. I have not fully explored the effects of changing a class's superclass post the creation of an instance of that class, however, so take caution if you are to do this at any point past +load or +initialize.

    This may make it difficult to call methods on super, but this could be gotten around by simply declaring a category on NSObject (or whichever superclass you choose to inherit from in your implementation).