myki
myki

Reputation: 763

Proper subclassing of class clusters

After watching iOS tech talks and reading up on class clusters I decided to extract legacy iOS 6 code to a private subclass:

@interface MyUIView : UIView @end           // public
@interface MyUIViewiOS6 : MyUIView @end     // private
@interface MyUIViewiOS7 : MyUIView @end     // private

@implementation MyUIView
+ (id)alloc
{
    // Don't loop on [super alloc]
    if ([[self class] isSubclassOfClass:[MyUIView class]] &&
        ([self class] != [MyUIViewiOS6 class]) &&
        ([self class] != [MyUIViewiOS7 class]))
    {
        if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
            return [MyUIViewiOS6 alloc];
        } else {
            return [MyUIViewiOS7 alloc];
        }
    }

    return [super alloc];
}
// Common implementation
@end

@implementation MyUIViewiOS6
// Legacy code
@end


@implementation MyUIViewiOS7
// iOS specific code
@end

This implementation works well until I want subclass MyUIView. For example if I create a subclass:

@interface MyRedUIView : MyUIView @end

and then init it like that:

[[MyRedUIView alloc] init]

object of type MyUIViewiOS6 or MyUIViewiOS7 will be allocated instead. Is there a way I can adapt this pattern to support subclassing, so that superclass of MyRedUIView is dynamically switched to MyUIViewiOS6 or MyUIViewiOS7?

Upvotes: 4

Views: 185

Answers (1)

Jason
Jason

Reputation: 13986

You've reached the classic double-inheritance problem. You want to be either a RedUIView or GreenUIView and be either a MyUIViewiOS6 or a MyUIViewiOS7 view.

Since objective-c does not support double-inheritance, you'll have to decide the difference between what you are, and how you act. Anything that determines what you are, you put in the class. Anything that determines how you act goes into a @protocol which then can be implemented.

I would subclass MyUIView since MyUIViewiOS6 and MyUIViewiOS7 correspond to who you are, and then implement a Red or Green protocol for certain functionality:

@interface MyRedUIView : MyUIView<RedProtocol> @end

You can check to see if this class conforms to a specific protocol:

 if ([class conformsToProtocol:@protocol(RedProtocol)]) {
    self.color = [UIColor redColor];
 }

If both of them really are who you are, then you have to use four separate classes.


Here's an example using categories. Assuming that you have MyUIView as specified in the question:

GreenView.h

#import "MyUIView.h"
#import "Green.h"

@interface MyUIView (GreenUIView) <Green>

-(BOOL) isGreen;

@end

@interface GreenView : MyUIView @end

GreenView.m

#import "GreenView.h"

@implementation MyUIView (GreenUIView)

-(BOOL) isGreen{
    return [self conformsToProtocol:@protocol(Green)];
}
@end

@implementation GreenView @end

Green.h

@protocol Green <NSObject> @end

AppDelegate.m

#import "GreenView.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    GreenView* view = [[GreenView alloc] init];
    NSLog(@"%@", [view isGreen]?@"yes":@"no");
    return YES;
}

@end

Upvotes: 2

Related Questions