实现copyWithZone的最佳实践:


77

我正在尝试清除有关实现的一些想法copyWithZone:,任何人都可以对以下内容发表评论...

// 001: Crime is a subclass of NSObject.
- (id)copyWithZone:(NSZone *)zone {
    Crime *newCrime = [[[self class] allocWithZone:zone] init];
    if(newCrime) {
        [newCrime setMonth:[self month]];
        [newCrime setCategory:[self category]];
        [newCrime setCoordinate:[self coordinate]];
        [newCrime setLocationName:[self locationName]];
        [newCrime setTitle:[self title]];
        [newCrime setSubtitle:[self subtitle]];
    }
    return newCrime;
}

// 002: Crime is not a subclass of NSObject.
- (id)copyWithZone:(NSZone *)zone {
    Crime *newCrime = [super copyWithZone:zone];
    [newCrime setMonth:[self month]];
    [newCrime setCategory:[self category]];
    [newCrime setCoordinate:[self coordinate]];
    [newCrime setLocationName:[self locationName]];
    [newCrime setTitle:[self title]];
    [newCrime setSubtitle:[self subtitle]];
    return newCrime;
}

在001中:

  1. 最好直接写类名[[Crime allocWithZone:zone] init]还是应该使用[[[self Class] allocWithZone:zone] init]

  2. 可以[self month]用来复制iVar,还是应该直接访问iVar _month

Answers:


100
  1. 您应该始终[[self class] allocWithZone:zone]确保使用适当的类创建副本。您为002给出的示例准确说明了原因:子类将调用[super copyWithZone:zone]并期望取回适当类的实例,而不是超类的实例。

  2. 我直接访问ivars,因此不必担心以后可能会添加到属性设置器中的任何副作用(例如,生成通知)。请记住,子类可以随意重写任何方法。在您的示例中,每个ivar发送两个额外的消息。我将实现如下:

码:

- (id)copyWithZone:(NSZone *)zone {
    Crime *newCrime = [super copyWithZone:zone];
    newCrime->_month = [_month copyWithZone:zone];
    newCrime->_category = [_category copyWithZone:zone];
    // etc...
    return newCrime;
}

当然,无论您是复制,保留还是仅分配它们,都应反映设置者的工作。


36
选择两种方法中的哪一种取决于超类是否实现NSCopying。例如,NSObject不这样做,因此调用[super copyWithZone: zone]将引发异常。
Costique 2012年

它说/Users/ws403216/Desktop/Demo/Demo/Crime.m:21:28:'NSObject'的不可见@interface声明选择器'copyWithZone:'在我的情况下,Crime.m的超类是NSObject。
Nitin Malguri 2014年

11
@NitinMalguri正如前面的注释所指出的,仅[super copyWithZone:zone]当父类支持NSCopying时才应调用,否则应按要求调用[[[self class] allocWithZone:zone] init]和复制字段。
托尼

2
默认的复制行为应该是浅表副本,但是您提供了深层副本的解决方案。浅表副本和深表副本之间的区别是:对象的浅表副本将仅将引用复制到原始数组的对象,并将其放置到新数组中。深拷贝实际上将复制对象中包含的各个对象。通过向每个对象发送“ copyWithZone:”消息来完成此操作。
三叉戟


6

copyWithZone:使用SDK提供的对象的方法的默认复制行为是“浅复制”。这意味着,如果你调用copyWithZone:NSString对象,它会创建一个浅表副本,但不深拷贝。浅拷贝和深拷贝之间的区别是:

对象的浅表副本只会将引用复制到原始数组的对象中,并将其放入新数组中。

深拷贝实际上将复制对象中包含的各个对象。通过copyWithZone:在您的自定义类方法中向每个对象发送消息来完成此操作。

INSHORT:要获取浅表副本,请调用retainstrong对所有实例变量进行调用。要获得深层复制,请调用copyWithZone:自定义类copyWithZone:实现中的所有实例变量。现在,您可以选择。


0

如何实现深层复制:

/// Class Foo has two properties: month and category
- (id)copyWithZone:(NSZone *zone) {
    Foo *newFoo;
    if ([self.superclass instancesRespondToSelector:@selector(copyWithZone:)]) {
        newFoo = [super copyWithZone:zone];
    } else {
        newFoo = [[self.class allocWithZone:zone] init];
    }
    newFoo->_month = [_month copyWithZone:zone];
    newFoo->_category = [_category copyWithZone:zone];
    return newFoo;
}

-1

这是我的模型。

#import <Foundation/Foundation.h>
@interface RSRFDAModel : NSObject


@property (nonatomic, assign) NSInteger objectId;

@property (nonatomic, copy) NSString *name;

@property (nonatomic, strong) NSArray<RSRFDAModel *> *beans;


@end


#import "RSRFDAModel.h"

@interface RSRFDAModel () <NSCopying>

@end

@implementation RSRFDAModel 


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

    model.objectId = self.objectId;
    model.name = self.name;
    model.beans = [self.beans mutableCopy];

    return model;
}

@end
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.