4 Stimmen

Verwendung von NSMutableDictionary als Backing Store für Eigenschaften

Ich bin auf der Suche nach einem kurzen Weg der Einstellung meiner Eigenschaften direkt zu einem NSMutableDictionary, die eine Instanzvariable ist. dh:

KVCModle.h:

@interface KVModel : NSObject {
    NSMutableDictionary * data;
}
@property(nonatomic,assign)NSString * string1;
@property(nonatomic,assign)NSString * string2;
@end

KVCModel.m

#import "KVModel.h"

@implementation KVModel

-(id)init
{
    self = [super init];
    if(self)
    {
        data = [[NSMutableDictionary alloc] init];
    }
    return self;
}

-(NSString *)string1
{
    return [data objectForKey:@"string1"];
}
-(NSString *)string2
{
    return [data objectForKey:@"string2"];
}
-(void)setString1:(NSString *)_string1
{
    [data setObject:_string1 forKey:@"string1"];
}
-(void)setString2:(NSString *)_string2
{
    [data setObject:_string2 forKey:@"string2"];
}
-(void)dealloc
{
    [data release];
    [super dealloc];
}

@end

Ich habe versucht, Folgendes zu überschreiben setValue:ForKey: y valueForKey: aber diese werden nicht aufgerufen, sondern ermöglichen es Ihnen, Eigenschaften direkt festzulegen, ohne die Eigenschaftssyntax zu verwenden.

Ich habe in der Vergangenheit Präprozessormakros erstellt, um dies zu ermöglichen, aber ich bin überhaupt nicht an Tipparbeit interessiert und möchte sie in Zukunft so weit wie möglich vermeiden. Gibt es eine mir unbekannte Möglichkeit, dies zu erreichen?

Ich habe darüber nachgedacht, NSManagedObject zu verwenden, aber ich bin nicht sicher, ob ich das, was ich will, damit erreichen kann. EDIT: Quelle

8voto

Peter Hosey Punkte 94684

Wenn Sie versuchen, auf die Eigenschaften mit Code wie foo = obj.foo y obj.foo = foo Deshalb funktioniert es nicht.

Die Syntax für den Eigenschaftszugriff ist gleichbedeutend mit der Nachrichtensyntax; erstere ist genau dasselbe wie foo = [obj foo] und letzteres ist genau dasselbe wie [obj setFoo:foo] . Es gibt keinen KVC-Code zum Abfangen. Eigenschaften befinden sich auf der Sprachebene, KVC auf der Rahmenebene.

Sie müssen stattdessen die Accessor-Nachrichten abfangen. Erwägen Sie die Implementierung von die resolveInstanceMethod: Klassenmethode in dem Sie den Selektor "auflösen", indem Sie der Klasse eine Methodenimplementierung mit Hilfe der Objective-C-Laufzeit-API hinzufügen. Sie können die gleiche(n) Implementierung(en) für viele verschiedene Selektoren hinzufügen.

Für Ihren Zweck sollten Sie eine Funktion oder Methode haben, die den Selektor untersucht (mit NSStringForSelector und normale NSString-Untersuchungstechniken) und gibt zwei Fakten zurück: (1) den Eigenschaftsnamen und (2) ob es sich um einen Getter ( foo , isFoo ) oder Setzer ( setFoo: ). Dann haben Sie zwei weitere Methoden, eine davon ein dynamischer Getter und die andere ein dynamischer Setter. Wenn der Selektor einen Getter nennt, fügen Sie ihn mit Ihrer dynamic-getter-Methode hinzu; wenn der Selektor einen Setter nennt, fügen Sie ihn mit Ihrer dynamic-setter-Methode hinzu.

Wie funktionieren also die Methoden "dynamic-getter" und "-setter"? Sie müssen wissen, welche Eigenschaft dynamisch geholt und gesetzt werden soll, aber sie müssen auch keine Argumente (getter) oder ein Argument (setter, der den Wert nimmt) annehmen, um der ursprünglichen Nachricht über den Eigenschaftszugriff zu entsprechen. Sie fragen sich vielleicht, woher diese generischen Implementierungen wissen, welche Eigenschaft sie holen oder setzen sollen. Die Antwort lautet: Es steht im Selektor! Der zum Senden der Nachricht verwendete Selektor wird als verstecktes Argument an die Implementierung übergeben _cmd untersuchen Sie diesen Selektor auf die gleiche Weise wie zuvor, um den Namen der Eigenschaft zu extrahieren, die Sie dynamisch abrufen oder festlegen möchten. Dann sollte der dynamische Getter Folgendes senden [data objectForKey:keyExtractedFromSelector] und der dynamische Setzer sollte senden [data setObject:newValue forKey:keyExtractedFromSelector] .

Zwei Vorbehalte:

  1. Sie können immer noch Beschwerden vom Compiler erhalten, wenn Sie die Property-Access-Syntax verwenden, um auf eine "Eigenschaft" zuzugreifen, die Sie nicht in der Klasse deklariert haben @interface . Das ist normal und beabsichtigt; man sollte die Property-Access-Syntax eigentlich nur für den Zugriff auf bekannte formale Eigenschaften verwenden. Was Sie tun, ist technisch gesehen ein Missbrauch der Property-Access-Syntax, auch wenn es mir Spaß gemacht hat, sie zu lösen.
  2. Dies funktioniert nur bei Objektwerten. KVC übernimmt das Boxing und Unboxing für primitive Werte, wie z. B. Ganzzahlen; da KVC nicht beteiligt ist, gibt es kein freies Boxing und Unboxing. Wenn Sie formale Eigenschaften deklariert haben (siehe 1), müssen Sie diese mithilfe der Objective-C-Laufzeit-API introspektieren und das Boxing und Unboxing selbst mit Ihren Ergebnissen durchführen.

6voto

Dave DeLong Punkte 240835

Das hat mich neugierig gemacht, also habe ich den Vorschlag von Peter Hosey aufgegriffen und die folgenden Funktionen außer Kraft gesetzt +resolveInstanceMethod: um die Getter und Setter zu erzeugen. Ich habe das resultierende Objekt ( DDDynamicStorageObject ) in ein Github-Repository:

https://github.com/davedelong/Demos

3voto

nielsbot Punkte 15742

Was Sie im Grunde wollen, ist Ihre eigene Implementierung der NSManagedObject-Maschinerie. Ich habe etwas ähnliches gemacht. Schauen Sie hier: https://gist.github.com/954035 HTH

(Der Code wurde aktualisiert, um die Abhängigkeit von der nicht existierenden NSString+Utilities.h zu entfernen)

(Fehlendes Makro ReleaseAndZero() hinzugefügt)

0voto

Jerry Jones Punkte 5383

Um Himmels willen - verwenden Sie ein NSDictionary nicht als Ort, an dem Sie jede denkbare Eigenschaft eines Modellobjekts unterbringen können. Ivars sind einfacher zu debuggen und viel klarer für andere Entwickler (einschließlich Ihres zukünftigen Ichs).

Wenn Sie ein Wörterbuch verwenden wollen, verwenden Sie ein Wörterbuch und einige statisch definierte Schlüssel - aber wenn Sie ein Modellobjekt wollen, verwenden Sie einige Ivar

0voto

Vincent Punkte 1

Ich stehe heute vor dem gleichen Problem wie Sie. So finde ich Ihre Frage hier gepostet.

In den obigen Antworten wurde die +resolveInstanceMethod: ist ein bisschen schwierig für mich :)

Mein Verständnis ist, dass, solange wir die Eigenschaft einrichten, wir Getter und Setter-Methode haben würde, so dass ich die Setter-Methode verwenden, um es zu implementieren.

BDLink.h

@property (nonatomic, strong) NSString *type;
@property (nonatomic, strong) NSString *displayName;
@property (nonatomic, strong) NSString *linkURI;

BDLink.m

- (id)initWithLinkInfoDictionary:(NSDictionary *)linkInfoDict {

    for (NSString *key in linkInfoDict) {
        const char *rawName = [key UTF8String];
        NSString *setMethodString = [NSString stringWithFormat:@"set%c%s:", toupper(rawName[0]), (rawName+1)];
        SEL setMethod = NSSelectorFromString(setMethodString);

        if ([self respondsToSelector:setMethod]) {
            [self performSelector:setMethod withObject:linkInfoDict[key]];
        }
    }

    return self;
}

Ich hoffe, es ist hilfreich. Meine erste Antwort, :)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X