实施 NSCopying
Posted
技术标签:
【中文标题】实施 NSCopying【英文标题】:Implementing 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 个其他自定义对象的数组。
Vendor
和 Car
都是从字典中初始化的。我将添加其中一种方法,它们可能相关也可能不相关。
-(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
s 数组。这意味着我还需要在属于 Car
对象的 2 个数组中保存的类上实现它。
如果我能得到一些关于在Vendor
上实现NSCopying
协议的指导,我将不胜感激,我在任何地方都找不到这方面的任何教程。
【问题讨论】:
你读过 NSCopying 的文档吗?在需要的时候我发现它很清楚。 是的,阅读并重新阅读。我很少发现易于学习的苹果文档,尽管它们非常适合在编程时查找方法等。谢谢-代码 【参考方案1】:要实现NSCopying,您的对象必须响应-copyWithZone:
选择器。以下是您声明遵守它的方式:
@interface MyObject : NSObject <NSCopying>
然后,在你的对象的实现中(你的.m
文件):
- (id)copyWithZone:(NSZone *)zone
// Copying code here.
您的代码应该做什么?首先,创建对象的一个新实例——您可以调用[[[self class] alloc] init]
来获取当前类的初始化对象,这对于子类化很有效。然后,对于任何支持复制的NSObject
子类的实例变量,您可以为新对象调用[thatObject copyWithZone:zone]
。对于原始类型(int
、char
、BOOL
和朋友)只需将变量设置为相等。因此,对于您的目标供应商,它看起来像这样:
- (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
【讨论】:
以上是关于实施 NSCopying的主要内容,如果未能解决你的问题,请参考以下文章