Programmieren mit Swift - Für macOS und iOS
Programmieren mit Swift - Für macOS und iOS
Ergänzung : NSCopying

Wenn es die Probleme mit den NSPopUpButton nicht gäbe, könnte man schnell auf die Idee kommen, das rpgCharacter-Objekt direkt mit den Daten des archivierten Objektes zu überschreiben.
rpgCharacter = [NSKeyedUnarchiver unarchiveObjectWithFile:filename];
Diese Zuweisung wird auch funktionieren, wie Sie mit der testBinding-Methode sehr schnell selbst überprüfen können. Allerdings wird sich die grafische Oberfläche nicht aktualisieren. Dafür ist eine etwas andere Schreibweise nötig. Key-Value-Observing muss ausgelöst werden.
[self setRpgCharacter:[NSKeyedUnarchiver unarchiveObjectWithFile:filename]];
Zwar wird diese Anweisung nicht funktionieren, da die zugehörige Accessor-Methode setRpgCharacter nicht existiert, aber das sollte für Sie kein Problem mehr darstellen. Trotzdem wird die Anwendung nicht kompilieren und Schuld daran ist das Wort copy in der Methode.
- (void)setRpgCharacter:(Character *)value {
    if (rpgCharacter != value) {
        [rpgCharacter release];
        rpgCharacter = [value copy];
    }
}
Man könnte jetzt natürlich behaupten, dass man gar keine Kopie des Objektes braucht, und in diesem Fall mag das sogar stimmen. Aber wenn Xcode die Accessor-Methode auf diese Art erstellt, sollte man sich auch ein wenig über die Hintergründe Gedanken machen.

Ein Objekt zu kopieren ist keine triviale Angelegenheit, denn ein Objekt, wie in diesem Fall rpgCharacter, kann selbst aus weiteren Objekten bestehen. Und da Objekte mit Speicherzeigern arbeiten, kann das etwas kompliziert werden.

Es gibt im Grunde zwei Arten, ein Objekt zu kopieren. Da ist zum Ersten die so genannte „Flache Kopie“ (shallow copy), bei der man einfach bestimmt, dass alle Instanzvariablen von Objekt A (wenn es auch Objekte sind) auf den gleichen Speicherplatz verweisen wie die Instanzvariablen von Objekt B. Dass sich die Objekte dann wechselseitig beeinflussen, kann gewollt sein, ist aber bei einer flachen Kopie auch nicht zu vermeiden.
Die zweite Möglichkeit ist die einer „Tiefen Kopie“ (deep copy), bei der dann wirklich die Variablenwerte kopiert werden und man ein eigenständiges und unabhängiges Objekt erhält.

Egal für welchen Weg man sich entscheidet, um ein Objekt durch den Aufruf copy zu kopieren, ist es nötig, dass die Klasse das Protokoll NSCopying implementiert. Für die Klasse Character bedeutete das:
@interface Character : NSObject {
Die zu diesem Protokoll gehörige Methode ist
-(id) copyWithZone:(NSZone *)zone
Wie aber geht man nun damit um, wenn man beispielsweise eine tiefe Kopie eines Character-Objektes machen will? Zuerst muss ein neues Objekt angelegt werden.
Character *copyCharacter;
copyCharacter = [[[
self class] allocWithZone:zone] init];
In diesen Zeilen geschehen schon eine ganze Menge Dinge, die Anweisungen sind ja auch sehr verschachtelt. Zuerst muss die Klasse des zu erstellenden Objekts mit [self class] ermittelt werden. Man könnte natürlich auch direkt ein Objekt vorgeben, da man ja genau weiss, womit man es hier zu tun hat.
copyCharacter = [[Character allocWithZone:zone] init];
Auch diese Anweisung würde funktionieren, solange man keine Klasse erstellt, die von Character abgeleitet ist. Dann könnte es beim Kopieren Probleme geben, weil die Klasse genau vorgegeben ist. Der als NSZone mitgesendete Parameter beschreibt übrigens einen Speicherbereich.

Hat man eine Kopie des Objektes, gilt es nun alle Instanzvariablen zu übertragen.
-(id) copyWithZone:(NSZone *)zone
{
    Character *copyCharacter;
    copyCharacter = [[[self class] allocWithZone:zone] init];

    [copyCharacter setStrength:[self strength]];
    [copyCharacter setDexterity:[self dexterity]];
    [copyCharacter setConstitution:[self constitution]];
    [copyCharacter setIntelligence:[self intelligence]];
    [copyCharacter setWisdom:[self wisdom]];
    [copyCharacter setCharisma:[self charisma]];
    [copyCharacter setCharacterName:[self characterName]];
    [copyCharacter setRace:[self race]];
    [copyCharacter setCharClass:[self charClass]];
    [copyCharacter setImage:[self image]];

    return copyCharacter;
}
Mit dieser Erweiterung weiss die Klasse Character jetzt, wie sie Kopien von sich selbst anfertigen kann, und jetzt wird auch folgende Anweisung funktionieren.
[self setRpgCharacter:[NSKeyedUnarchiver unarchiveObjectWithFile:filename]];
Probieren Sie es ruhig aus. Bis auf die Werte in den NSPopUpButton werden alle Informationen sofort nach dem Laden dargestellt.