实现copyWithZone时的最佳实践:

时间:2012-03-28 12:07:38

标签: iphone objective-c cocoa-touch copywithzone

我正在努力澄清一些关于实施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]复制iVars,还是应该直接访问iVars,_month

4 个答案:

答案 0 :(得分:96)

  1. 您应始终使用[[self class] allocWithZone:zone]确保使用相应的类创建副本。您为002提供的示例显示了原因:子类将调用[super copyWithZone:zone]并期望返回相应类的实例,而不是超类的实例。

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

  3. 代码:

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

    当然,无论是复制ivars,保留它们,还是只是分配它们都应该反映出制定者所做的事情。

答案 1 :(得分:6)

使用SDK提供的对象的copyWithZone:方法的默认复制行为是"浅拷贝"。这意味着如果您在copyWithZone:对象上调用NSString,它将创建浅拷贝而不是深拷贝。浅拷贝和深拷贝之间的区别是:

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

深层复制实际上会复制对象中包含的各个对象。这是通过在自定义类方法中向每个单独的对象发送copyWithZone:消息来完成的。

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

答案 2 :(得分:0)

这是我的模特。

#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

答案 3 :(得分: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;
}