Reputation: 17844
Let's say I have two Classes like so:
Car
{
NSInteger wheels;
NSInteger bumpers;
}
+ (Car *)carWithData:(NSDictionary *)carData;
Lexus : Car
{
GPS *navigation;
}
+ (Lexus *)carWithData:(NSDictionary *)carData;
carWithData:
is a simple helper method that creates an instance of Car
populated with variables from carData
. Lexus' version would also set the navigation
data.
How would Lexus' carWithData
look like without duplicating code from Car
?
Upvotes: 1
Views: 832
Reputation: 12095
There's another way, which is more generic, by using the NSCoding
protocol and NSKeyedArchiver
.
If the object you want to copy into a subclass of yours implements the NSCoding
protocol, which is the case for many NS... based classes, the following totally legal and safe trick can be used:
// Assumptions:
// copyFrom is an object of ClassA
// We have a ClassB that is a subclass of ClassA
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[copyFrom encodeWithCoder:arch]; // this archives its properties
[arch finishEncoding];
NSKeyedUnarchiver *ua = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
ClassB *ourCopy = [[ClassB alloc] initWithCoder:ua]; // this restores the properties into our new object
Upvotes: 0
Reputation: 29562
This is accomplished by calling super
's implementation of init…
in the init method:
//Car.m:
- (id)initWithData:(NSDictionary *)carData {
self = [super init];
if (self) {
//setup generic car properties:
self.wheels = [carData objectForKey:@"wheels"]; //example
self.bumpers = [carData objectForKey:@"bumpers"]; //example
}
return self;
}
+ (id)carWithData:(NSDictionary *)carData {
return [[[self alloc] initWithData:carData] autorelease];
}
//Lexus.m:
- (id)initWithData:(NSDictionary *)carData {
//this call to super is where the car's generic properties get initialized:
self = [super initWithWithData:carData];
if (self) {
//setup lexus car properties:
self.navigation = [carData objectForKey:@"navigation"]; //example
}
return self;
}
//there is no need to override super's [carWithData:] method as it's only a wrapper anyway.
Also note that both the initWith…
and carWith…
methods return id
, not Car
or Lexus
.
The way your code is set up you end up with casting problems, where [Lexus carWithData:dataDict]
does return an object of class Lexus
, but the compiler doesn't know about it, as it expects a Car
.
Upvotes: 5
Reputation: 55583
Here would be my solution:
// interface
-(id) initWithCarData:(NSDictionary *) carData;
+(Car *) carWithCarData:(NSDictionary *) carData;
// car implementation
-(id) initWithCarData:(NSDictionary *) carData
{
if (self = [super init])
{
// initialize car data
}
return self;
}
+(Car *) carWithCarData:(NSDictionary *) carData
{
// note that 'self' here is the current class,
// there is no need to overwrite this method in the subclass
return [[self alloc] initWithCarData:carData];
}
// lexus implementation
-(id) initWithCarData:(NSDictionary *) carData
{
// initialize the variables that the superclass recognizes
if (self = [super initWithCarData:carData])
{
// initialize the lexus data
}
return self;
}
So, when you call [Lexus carWithCarData:myData]
it ends up calling the Lexus
's init
method, not the Car
's.
Upvotes: 0
Reputation: 38728
You would not define the methods with different signatures like:
+ (Car *)carWithData:(NSDictionary *)carData;
+ (Lexus *)carWithData:(NSDictionary *)carData;
you should instead use
+ (id)carWithData:(NSDictionary *)carData;
The implementation of the subclass would then look like
- (id)initWithData:(NSDictionary *)carData;
{
self = [super initWithData:carData];
if (self) {
_navigation = [carData valueForKey:@"navigation"];
}
return self;
}
+ (id)carWithData:(NSDictionary *)carData;
{
return [[[self alloc] initWithCarData:carData] autorelease];
}
Upvotes: 0