Best Practice bei der Implementierung von copyWithZone:

77

Ich versuche ein paar Dinge in meinem Kopf über die Implementierung zu klären copyWithZone:, kann jemand Folgendes kommentieren ...

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

In 001:

  1. Ist es am besten, den Klassennamen direkt zu schreiben [[Crime allocWithZone:zone] init]oder sollte ich verwenden [[[self Class] allocWithZone:zone] init]?

  2. Ist es in [self month]Ordnung, die iVars zu kopieren, oder sollte ich direkt auf die iVars zugreifen, dh _month?

Fuzzygoat
quelle

Antworten:

100
  1. Sie sollten immer verwenden [[self class] allocWithZone:zone], um sicherzustellen, dass Sie eine Kopie mit der entsprechenden Klasse erstellen. Das Beispiel, das Sie für 002 geben, zeigt genau, warum: Unterklassen rufen [super copyWithZone:zone]eine Instanz der entsprechenden Klasse auf und erwarten, dass sie zurückkommt, nicht eine Instanz der Superklasse.

  2. Ich greife direkt auf die ivars zu, sodass ich mir keine Gedanken über Nebenwirkungen machen muss, die ich später dem Eigenschaftensetzer hinzufügen könnte (z. B. das Generieren von Benachrichtigungen). Beachten Sie, dass Unterklassen jede Methode überschreiben können. In Ihrem Beispiel senden Sie zwei zusätzliche Nachrichten pro ivar. Ich würde es wie folgt implementieren:

Code:

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

Ob Sie die Ivars kopieren, behalten oder nur zuweisen, sollte natürlich das widerspiegeln, was die Setter tun.

Tony
quelle
36
Welcher der beiden Ansätze zu wählen ist, hängt davon ab, ob die Oberklasse implementiert NSCopying. Zum Beispiel NSObjectnicht, so dass ein Aufruf [super copyWithZone: zone]eine Ausnahme auslöst.
Costique
Es heißt /Users/ws403216/Desktop/Demo/Demo/Crime.m:21:28: Keine sichtbare @ Schnittstelle für 'NSObject' deklariert den Selektor 'copyWithZone:' Superklasse von Crime.m in meinem Fall ist NSObject.
Nitin Malguri
11
@NitinMalguri Wie im vorherigen Kommentar erwähnt, sollten Sie nur aufrufen, [super copyWithZone:zone]wenn die übergeordnete Klasse NSCopying unterstützt. Andernfalls sollten Sie [[[self class] allocWithZone:zone] init]Felder nach Bedarf aufrufen und kopieren.
Tony
2
Das Standardkopierverhalten sollte eine flache Kopie sein, Sie haben jedoch eine Lösung für eine tiefe Kopie bereitgestellt. Der Unterschied zwischen flacher und tiefer Kopie besteht darin: Eine flache Kopie eines Objekts kopiert nur die Verweise auf die Objekte des ursprünglichen Arrays und platziert sie im neuen Array. Eine tiefe Kopie kopiert tatsächlich die einzelnen im Objekt enthaltenen Objekte. Dazu senden Sie jedem einzelnen Objekt die Nachricht "copyWithZone:".
Dreizack
das kann Missverständnisse NSCoping prot
kokos8998
6

Das Standardkopierverhalten der copyWithZone:Methode mit von SDK bereitgestellten Objekten ist "flache Kopie". Das heißt, wenn Sie copyWithZone:ein NSStringObjekt aufrufen , wird eine flache, aber keine tiefe Kopie erstellt. Der Unterschied zwischen flacher und tiefer Kopie ist:

Eine flache Kopie eines Objekts kopiert nur die Verweise auf die Objekte des ursprünglichen Arrays und platziert sie im neuen Array.

Eine tiefe Kopie kopiert tatsächlich die einzelnen im Objekt enthaltenen Objekte. Dazu senden Sie jedem einzelnen Objekt die copyWithZone:Nachricht in Ihrer benutzerdefinierten Klassenmethode.

INSHORT: Um eine flache Kopie zu erhalten, rufen Sie retainoder strongalle Instanzvariablen auf. Um eine tiefe Kopie zu erhalten, rufen Sie copyWithZone:alle Instanzvariablen in Ihrer benutzerdefinierten Klassenimplementierung auf copyWithZone:. Jetzt haben Sie die Wahl.

Dreizack
quelle
0

Wie wäre es mit diesem, der Deep Copy implementiert:

/// 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;
}
N.Lee
quelle
-1

Das ist mein Modell.

#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
ylgwhyh
quelle