实施NSCopying

时间:2010-11-03 16:28:19

标签: iphone ios objective-c 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包含2个其他自定义对象数组。

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个对象的数组。我认为我需要在NSCopying上实施Vendor协议,这可能意味着我需要在Car上实施该协议,因为Vendor拥有Car数组秒。这意味着我还需要在属于Car对象的2个数组中保存的类上实现它。

如果我能在NSCopying上获得有关实施Vendor协议的指导,我真的很感激,我无法在任何地方找到任何教程。

3 个答案:

答案 0 :(得分:178)

要实施NSCopying,您的对象必须回复-copyWithZone:选择器。以下是您声明符合它的方式:

@interface MyObject : NSObject <NSCopying> {

然后,在对象的实现中(您的.m文件):

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

您的代码应该怎么做?首先,创建一个对象的新实例 - 您可以调用[[[self class] alloc] init]来获取当前类的初始化obejct,这对于子类化很有效。然后,对于支持复制的NSObject子类的任何实例变量,可以为新对象调用[thatObject copyWithZone:zone]。对于原始类型(intcharBOOL和朋友),只需将变量设置为相等即可。因此,对于您的obejct供应商,它看起来像这样:

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

答案 1 :(得分:6)

此答案与已接受的答案类似,但使用allocWithZone:并为ARC更新。 NSZone是分配内存的基础类。虽然忽略NSZone可能适用于大多数情况,但仍然不正确。

要正确实现NSCopying,您必须实现一个协议方法,该方法分配对象的新副本,其属性与原始值相匹配。

在标头的接口声明中,指定您的类实现NSCopying协议:

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

在.m实现中添加一个-(id)copyWithZone方法,类似于以下内容:

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

答案 2 :(得分:1)

Swift版本

只需致电object.copy()即可创建副本。

我没有将copy()用于值类型,因为它们是“自动”复制的。但我必须将copy()用于class类型。

我忽略了NSZone参数,因为docs表示已弃用:

  

忽略此参数。内存区域不再使用   目标C

另外,请注意这是一个简化的实现。 如果您有子类,它会有点诡计,您应该使用动态类型: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
    }
}