2012-03-28 69 views
73

我想澄清有關實現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

回答

94
  1. 你應該總是使用[[self class] allocWithZone:zone],以確保您正在創建使用適當的類的副本。你爲002給出的例子顯示了原因:子類將調用[super copyWithZone:zone],並期望取回適當類的實例,而不是超類的實例。

  2. 我直接訪問ivars,因此我不需要擔心後面可能會添加到屬性設置器(例如,生成通知)的任何副作用。請記住,子類可以自由覆蓋任何方法。在你的例子中,你每個伊娃發送兩條額外的消息。我想如下實現它:

代碼:

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

當然,無論你複製實例變量,留住他們,或者只是爲它們分配應反映什麼setter方法做。

+32

選擇哪兩種方法取決於超是否實現'NSCopying'。例如,'NSObject'不會,所以調用'[super copyWithZone:zone]'會拋出異常。 – Costique 2012-03-29 05:29:32

+0

它說/用戶/ ws403216 /桌面/演示/演示/犯罪。m:21:28:'NSObject'沒有可見的@interface聲明選擇器'copyWithZone:'在我的情況下,超類的Crime.m是NSObject。 – 2014-04-30 07:05:30

+10

@NitinMalguri正如前面的評論指出的,如果父類支持NSCopying,你應該只調用'[super copyWithZone:zone]',否則你應該調用'[[[self class] allocWithZone:zone] init]'並複製字段按要求。 – Tony 2014-05-08 17:35:41

6

使用SDK提供的對象的copyWithZone:方法的默認複製行爲是「淺拷貝」。這意味着如果您在NSString對象上調用copyWithZone:,它將創建淺拷貝而不是深拷貝。淺拷貝和深拷貝之間的區別在於:

對象的淺拷貝只會將引用複製到原始數組的對象,並將它們放入新數組中。

深層複製實際上會複製對象中包含的單個對象。這是通過發送每個單獨的對象在您的自定義類方法中的copyWithZone:消息完成的。

INSHORT:要獲得淺拷貝,請在所有實例變量上調用retainstrong。要獲得深層副本,請在您的自定義類copyWithZone:實現中的所有實例變量上調用copyWithZone:。現在您可以選擇。

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