Code Code - 5 months ago 9
iOS Question

Implementing NSCopying

I've read the

NSCopying
docs but I am still very unsure about how to implement what is required.

My class
Vendor
:

@interface Vendor : NSObject
{
NSString *vendorID;
NSMutableArray *availableCars;
BOOL atAirport;
}

@property (nonatomic, copy) NSString *vendorID;
@property (nonatomic, retain) NSMutableArray *availableCars;
@property (nonatomic, assign) BOOL atAirport;

- (id)initFromVehVendorAvailsDictionary:(NSDictionary *)vehVendorAvails;

@end


The
Vendor
class has an array of objects called
Car
.

My
Car
object:

@interface Car : NSObject
{
BOOL isAvailable;
NSString *transmissionType;
NSMutableArray *vehicleCharges;
NSMutableArray *fees;
}

@property (nonatomic, assign) BOOL isAvailable;
@property (nonatomic, copy) NSString *transmissionType;
@property (nonatomic, retain) NSMutableArray *vehicleCharges;
@property (nonatomic, retain) NSMutableArray *fees;

- (id) initFromVehicleDictionary:(NSDictionary *)vehicleDictionary;

@end


So,
Vendor
holds an array of
Car
objects.
Car
holds 2 arrays of other custom objects.

Both
Vendor
and
Car
are init from a dictionary. I'll add one of these methods, they may or may not be relevant.

-(id)initFromVehVendorAvailsDictionary:(NSDictionary *)vehVendorAvails {

self.vendorCode = [[vehVendorAvails objectForKey:@"Vendor"]
objectForKey:@"@Code"];

self.vendorName = [[vehVendorAvails objectForKey:@"Vendor"]
objectForKey:@"@CompanyShortName"];

self.vendorDivision = [[vehVendorAvails objectForKey:@"Vendor"]
objectForKey:@"@Division"];

self.locationCode = [[[vehVendorAvails objectForKey:@"Info"]
objectForKey:@"LocationDetails"]
objectForKey:@"@Code"];

self.atAirport = [[[[vehVendorAvails objectForKey:@"Info"]
objectForKey:@"LocationDetails"]
objectForKey:@"@AtAirport"] boolValue];

self.venLocationName = [[[vehVendorAvails objectForKey:@"Info"]
objectForKey:@"LocationDetails"]
objectForKey:@"@Name"];

self.venAddress = [[[[vehVendorAvails objectForKey:@"Info"]
objectForKey:@"LocationDetails"]
objectForKey:@"Address"]
objectForKey:@"AddressLine"];

self.venCountryCode = [[[[[vehVendorAvails objectForKey:@"Info"]
objectForKey:@"LocationDetails"]
objectForKey:@"Address"]
objectForKey:@"CountryName"]
objectForKey:@"@Code"];

self.venPhone = [[[[vehVendorAvails objectForKey:@"Info"]
objectForKey:@"LocationDetails"]
objectForKey:@"Telephone"]
objectForKey:@"@PhoneNumber"];

availableCars = [[NSMutableArray alloc] init];

NSMutableArray *cars = (NSMutableArray *)[vehVendorAvails objectForKey:@"VehAvails"];

for (int i = 0; i < [cars count]; i++) {

Car *car = [[Car alloc] initFromVehicleDictionary:[cars objectAtIndex:i]];
[availableCars addObject:car];
[car release];
}

self.venLogo = [[[vehVendorAvails objectForKey:@"Info"]
objectForKey:@"TPA_Extensions"]
objectForKey:@"VendorPictureURL"];

return self;
}


So to summarize the scary problem.

I need to copy an array of
Vendor
objects. I believe I need to implement the
NSCopying
protocol on
Vendor
, which may mean I need to implement it also on
Car
since
Vendor
holds an array of
Car
s. That means I also need to implement it on the classes that are held in the 2 arrays belonging to the
Car
object.

I'd really appreciate it if I could get some guidance on implementing
NSCopying
protocol on
Vendor
, I can't find any tutorials on this anywhere.

Answer

To implement NSCopying, your object must respond to the -copyWithZone: selector. Here’s how you declare that you conform to it:

@interface MyObject : NSObject <NSCopying> {

Then, in your object’s implementation (your .m file):

- (id)copyWithZone:(NSZone *)zone
{
    // Copying code here.
}

What should your code do? First, create a new instance of the object—you can call [[[self class] alloc] init] to get an initialized obejct of the current class, which works well for subclassing. Then, for any instance variables that are a subclass of NSObject that supports copying, you can call [thatObject copyWithZone:zone] for the new object. For primitive types (int, char, BOOL and friends) just set the variables to be equal. So, for your obejct Vendor, it’d look like this:

- (id)copyWithZone:(NSZone *)zone
{
    id copy = [[[self class] alloc] init];

    if (copy) {
        // Copy NSObject subclasses
        [copy setVendorID:[[self.vendorID copyWithZone:zone] autorelease]];
        [copy setAvailableCars:[[self.availableCars copyWithZone:zone] autorelease]];

        // Set primitives
        [copy setAtAirport:self.atAirport];
    }

    return copy;
}