实施NSCopying


84

我已经阅读了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从字典初始化。我将添加这些方法之一,它们可能相关,也可能不相关。

-(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包含Cars数组。这意味着我还需要在属于该Car对象的2个数组中持有的类上实现它。

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


您阅读过NSCopying的文档吗?我发现需要的时候很清楚。
10年

4
是的,请阅读并重新阅读。我很少发现苹果文档易于学习,尽管它们非常适合在编程时查找方法等。谢谢-Code

Answers:


186

要实现NSCopying,您的对象必须响应-copyWithZone:选择器。声明符合条件的方式如下:

@interface MyObject : NSObject <NSCopying> {

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

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

您的代码应该做什么?首先,创建该对象的新实例-您可以调用[[[self class] alloc] init]以获取当前类的已初始化对象,该对象对子类非常有用。然后,对于NSObject支持复制的子类的任何实例变量,可以调用[thatObject copyWithZone:zone]新对象。对于原始类型(intcharBOOL和朋友)刚刚成立的变量是相等的。因此,对于您的对象供应商而言,它看起来像这样:

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

2
@Code:copy通常实现为像Jeff所示的浅表副本。您想要完整的深层副本(在其中复制所有内容,这是不寻常的,尽管并非不可想象)。深拷贝也带来更多麻烦,因此您通常要确保这确实是您想要的。
Chuck

3
您的代码中有一个问题,您在其中复制子类,因为copyWithZone:返回引用计数为1且没有自动释放的对象将导致泄漏。您需要至少添加一个自动发布。
马吕斯

22
不应该[[self class] alloc]使用它allocWithZone吗?很抱歉提出这个问题。
jweyrich 2012年

1
伙计们,我想通过使用ARC(因为任何应用程序支持的最低IOS为4.3),您不必担心发布和自动发布。
rishabh 2012年

1
@GeneralMike:这可能应该是一个单独的问题,但总的来说(请参阅我在那做的事?),您要确保在进行深度复制时从原始对象复制每个对象,并确保其-copy方法也可以进行深度复制。
杰夫·凯利2014年

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

迅捷版

刚打电话 object.copy()即可创建副本。

我不使用copy()值类型,因为它们是“自动”复制的。但是,我不得不使用copy()class类型。

我忽略了该NSZone参数,因为文档已弃用该参数:

该参数被忽略。存储区域不再由Objective-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
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.