Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specific Class type parameter in Objective-C

I want to accept a Class object as a parameter to a constructor of one of my classes. It needs to do a lot of custom work with it, and I want to abstract that away from the user of the class.

For example, let's say my class is a Dealership and I want to initialize it with any type of Vehicle.

So I have the following hierarchy:

Dealership : NSObject

Vehicle : NSObject
Truck : Vehicle
Van : Vehicle
Car : Vehicle

What I want to do is, inside Dealership, implement the following initializer:

- (id)initWithVehicle:(Class)aVehicle;

Except instead of accepting a generic Class, I'd like to restrict it to only Classes of type "Vehicle" (which would include all my inherited classes). I could just test for this property in the initializer, but it would be great if there was a way to enforce this at compile time instead of waiting for runtime feedback.

It seems you can reference Class to restrict to classes that implement a certain interface, so I could do some hackery there. Is there any way to refer to Class objects of a specific type though?

EDIT - Note that I edited the example classes because the original example was a bit misleading. The example is just for demonstration purposes, not the actual class hierarchy I'm working with.

like image 457
DougW Avatar asked May 03 '11 18:05

DougW


2 Answers

You can ensure using a protocol check:

- (id)initWithVehicle:(id<VehicleProtocol>)aVehicle;
...
}

Declaring an object as id tells the compiler that you don't care what type the object is, but you do care that it conforms to the specified VehicleProtocol protocol**.

like image 175
Cyprian Avatar answered Sep 29 '22 10:09

Cyprian


Not at compile-time, but you can release self and return nil if the class is invalid:

- (id)initWithCar: (Class)carClass {
    self = [super init];

    if (self) {
        if (![carClass isSubclassOfClass:[Car class]]) {
            [self release];
            self = nil;
        } else {
            // Normal initialization here.
        }
    }

    return self;
}

That's the closest you'll get to the sort of restriction you want.

But this sort of design suggests you need to rethink your class hierarchy. Rather than passing a subclass of Car, you should have a Manufacturer class. Something like this:

@interface Manufacturer : NSObject
+ (id)manufacturerWithName: (NSString *)name;

- (NSArray *)allCars;
@property (readonly) Car *bestsellingCar;
// etc.
@end

#define kManufacturerVolvo [Manufacturer manufacturerWithName: @"Volvo"]
#define kManufacturerToyota [Manufacturer manufacturerWithName: @"Toyota"]
// etc.

And then this initializer for Dealership:

- (id)initWithManufacturer: (Manufacturer *)aManufacturer;
like image 22
Jonathan Grynspan Avatar answered Sep 29 '22 11:09

Jonathan Grynspan