实施 NSCopying

我已经阅读了 NSCopying文档,但是我仍然不确定如何实现所需的内容。

我的班级 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

Vendor类有一个名为 Car的对象数组。

我的 Car对象:

@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

因此,Vendor包含一个 Car对象数组,而 Car包含两个其他自定义对象数组。

VendorCar都是来自字典的 init。我将添加这些方法中的一个,它们可能是相关的,也可能不是相关的。

-(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;
}

总结一下这个可怕的问题。

我需要复制 Vendor对象的数组。我相信我需要在 Vendor上实现 NSCopying协议,这可能意味着我也需要在 Car上实现它,因为 Vendor持有一个 Car数组。这意味着我还需要在属于 Car对象的2个数组中的类上实现它。

如果我能得到一些关于在 Vendor上实现 NSCopying协议的指导,我将非常感激,我在任何地方都找不到关于这方面的任何教程。

57412 次浏览

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 object 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;
}

This answer is similar to the accepted, but uses allocWithZone: and is updated for ARC. NSZone is foundation class for allocating memory. While ignoring NSZone might work for most cases, it is still incorrect.

To correctly implement NSCopying you must implement a protocol method which allocates a new copy of the object, with properties that match the values of the original.

In the interface declaration in the header, specify that your class implements the NSCopying protocol:

@interface Car : NSObject<NSCopying>
{
...
}

In the .m implementation add a -(id)copyWithZone method which looks something like the following:

- (id)copyWithZone:(NSZone*)zone
{
Car* carCopy = [[[self class] allocWithZone:zone] init];


if (carCopy)
{
carCopy.isAvailable = _isAvailable;
carCopy.transmissionType = _transmissionType;
... // assign all other properties.
}


return carCopy;
}

Swift Version

Just call object.copy() to create the copy.

I didn't use copy() for value types since those are copied "automatically." But I had to use copy() for class types.

I ignored the NSZone parameter because docs say it is deprecated:

This parameter is ignored. Memory zones are no longer used by Objective-C.

Also, please note that this is a simplified implementation. If you have subclasses it gets a bit tricker and you should use dynamic type: type(of: self).init(transmissionType: transmissionType).

class Vendor {
let vendorId: String
var availableCars: [Car] = []


init(vendorId: String) {
self.vendorId = vendorId
}
}


extension Vendor: NSCopying {
func copy(with zone: NSZone? = nil) -> Any {
let copy = Vendor(vendorId: vendorId)
if let availableCarsCopy = availableCars.map({$0.copy()}) as? [Car] {
copy.availableCars = availableCarsCopy
}
return copy
}
}


class Car {
let transmissionType: String
var isAvailable: Bool = false
var fees: [Double] = []


init(transmissionType: String) {
self.transmissionType = transmissionType
}
}


extension Car: NSCopying {
func copy(with zone: NSZone? = nil) -> Any {
let copy = Car(transmissionType: transmissionType)
copy.isAvailable = isAvailable
copy.fees = fees
return copy
}
}