【问题标题】:Implementing NSCopying实施 NSCopying
【发布时间】:2011-05-04 14:32:02
【问题描述】:

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

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

【问题讨论】:

  • 你读过 NSCopying 的文档吗?在需要的时候我发现它很清楚。
  • 是的,阅读并重新阅读。我很少发现易于学习的苹果文档,尽管它们非常适合在编程时查找方法等。谢谢-代码

标签: iphone ios objective-c nscopying


【解决方案1】:

要实现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;
}

【讨论】:

  • @Code: copy 通常实现为 Jeff 所示的浅拷贝。这是不寻常的——虽然不是不可想象的——你想要一个完整的深拷贝(所有的东西都被复制了)。深拷贝也麻烦很多,所以您通常要确定这确实是您想要的。
  • 复制子类的代码中存在问题,因为copyWithZone: 返回引用计数为 1 的对象并且没有自动释放,这将导致泄漏。您至少需要添加一个自动释放。
  • 不应该[[self class] alloc] 使用allocWithZone 代替吗?很抱歉提出这个问题。
  • @jweyrich 你可能是对的,但在典型使用中这并不重要。
  • 伙计们,我想通过使用ARC(因为任何应用程序支持的最低IOS是4.3),你不必担心发布和自动发布。
【解决方案2】:

此答案类似于接受的答案,但使用 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;
}

【讨论】:

    【解决方案3】:

    斯威夫特版本

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

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

    我忽略了NSZone 参数,因为docs 说它已被弃用:

    此参数被忽略。内存区域不再使用 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
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-12-17
      • 2011-08-18
      • 2015-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-24
      • 2011-05-27
      相关资源
      最近更新 更多