jcnnghm
jcnnghm

Reputation: 7432

Allocate or Initialize Subclass From Superclass

I've got a universal iOS app that I've been working on. For the most part, I've been handling device differences by creating subclasses, so RootViewController is subclassed by RootViewController-iPad and RootViewController-iPhone. The pattern starts in main.m, where I do device detection as follows:

int retVal;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
    retVal = UIApplicationMain(argc, argv, nil, @"SalesPadAppDelegate_iPad");
} else {
    retVal = UIApplicationMain(argc, argv, nil, @"SalesPadAppDelegate_iPhone");
}

This pattern is working quite well, and is minimize code reuse.

Further down the stack, I've got a series of view controllers that inherit from a common superclass, MasterDetailViewController. This superclass needs some device specific code, so I was thinking of subclassing that, and then ending up with the superclass automatically allocating or instantiating the device specific subclass that's most suitable.

Implementation wise, this looks like the following:

+(id)allocWithZone:(NSZone *)zone {
    if ([[self class] isEqual:[MasterDetailViewController_iPad class]] || [[self class] isEqual:[MasterDetailViewController_iPhone class]]) {
        return [super allocWithZone:zone];
    } else {
        if ([[UIDevice currentDevice] userInterfaceIdiom]==UIUserInterfaceIdiomPhone) {
            return [MasterDetailViewController_iPhone allocWithZone:zone];
        } else {
            return [MasterDetailViewController_iPad allocWithZone:zone];
        }
    }
}

This code doesn't work, and is kind of a demonstration of concept. Is there a way to do what I'm trying to do, or am I looking at things all wrong?

EDIT: Just to be clear, the data is the same between the super and subclasses. The only difference would be a couple of method implementations. The idea being that subclassing is cleaner than device specific spaghetti code.

Upvotes: 1

Views: 1092

Answers (1)

Anomie
Anomie

Reputation: 94794

Chances are your code isn't working because self inside a class method is the class object, so [self class] is the class of the class. Try just [self isEqual:[MasterDetailViewController_iPad class]] instead. You could also turn around the comparison, something like this:

+ (id)allocWithZone:(NSZone *)zone {
    // Allocating a subclass, don't interfere
    if (![self isEqual:[MasterDetailViewController class]]) {
        return [super allocWithZone:zone];
    }

    // Select an appropriate subclass to create instead
    if ([[UIDevice currentDevice] userInterfaceIdiom]==UIUserInterfaceIdiomPhone) {
        return [MasterDetailViewController_iPhone allocWithZone:zone];
    } else {
        return [MasterDetailViewController_iPad allocWithZone:zone];
    }
}

That might be a little easier to extend, if you ever have reason for more device-specific subclasses.

Note that there are other ways to do this. For example, instead of device-specific subclasses, the one general class could allocate and use a device-specific helper object to handle the few operations that need to be different (sort of like a delegate). Or, instead of overriding allocWithZone:, you could have the base class's init method release self and instead allocate and return a new object of the appropriate subclass. Or you could use a factory method to allocate the appropriate device-specific subclass.

Upvotes: 1

Related Questions