Objective-C: Instantiate Child Class From Parent

A common problem encountered (in any language) is how to instantiate a child class from an instance of a base parent in a generic way.  In Objective-C take for example:

1
2
3
4
5
6
7
8
9
10
11
@interface Vehicle : NSObject

@property (copy,nonatomic) NSString *name;

@end

@interface Motorcycle : Vehicle

@property (copy,nonatomic) NSNumber *engineCC;

@end

If we have an instance of Vehicle and we want to get an instance of a Motorcycle we could manually copy each property:

Manual Property Copy

1
2
3
4
5
6
Vehicle *vehicle = [Vehicle new];
vehicle.name = @"MyVehicle";

Motorcycle *motorcycle = [Motorcycle new];
motorcycle.name = vehicle.name;
motorcycle.engineCC = [NSNumber numberWithInt:200];

Seems simple enough in this case, but when the property count starts increasing so does the amount of typing. If only there were a better way…:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
+ (void) copyParent:(id)parent intoChild:(id) child
{
    id parentClass = [parent class];
    NSString *propertyName;
    unsigned int outCount, i;

    //Get a list of properties for the parent class.
    objc_property_t *properties = class_copyPropertyList(parentClass, &outCount);

    //Loop through the parents properties.
    for (i = 0; i < outCount; i++)
    {
        objc_property_t property = properties[i];

        //Convert the property to a string.
        propertyName = [NSString stringWithCString:property_getName(property) encoding:NSASCIIStringEncoding];

        //Get the parent's value for the property
        id value = [parent valueForKey:propertyName];

        //..and copy into the child.
        if ([value conformsToProtocol:@protocol(NSCopying)])
        {
            [child setValue:[value copy] forKey:propertyName];
        }
        else
        {
            [child setValue:value forKey:propertyName];
        }

    }
}

Note: Make sure to import objc/runtime.h:

1
#import <objc/runtime.h>

Sample Usage (assuming we put the method in a class called Utils):

1
2
3
4
5
Vehicle *vehicle = [Vehicle new];
vehicle.name = @"MyVehicle";

Motorcycle *motorcycle = [Motorcycle new];
[Utils copyParent:vehicle intoChild:motorcycle];
Tech

Comments