2016-07-26 27 views
4

In meinem Projekt habe ich eine Einstellungsklasse mit Eigenschaften mit benutzerdefinierten Sätzen, die auf NSUserDefaults zugreifen, um alles einfacher zu machen. Die Idee ist, dass Settings KlasseObjective-C Allgemeine Getter- und Setter-Methoden schreiben

@property NSString *name 

hat die benutzerdefinierten Getter hat, der den Namen Wert von NSUserDefaults und einen Setter bekommt, die dort den neuen Wert speichert. Auf diese Weise interagiere ich während des gesamten Projekts mit der Klasse "Einstellungen" nur, um benutzerdefinierte Einstellungen zu verwalten. Die Sache ist, dass es viel zu repetitiv erscheint, um alle Getter und Setter zu schreiben (ich habe ungefähr 50 Eigenschaften), und möchte einen Setter und einen Getter erstellen, der für alle Variablen funktionieren würde. Mein einziges Problem ist, wie man den Namen der Variablen innerhalb des Setter ergreift.

Die letzte Frage ist dann: ist es möglich, in einem Getter oder Setter herauszufinden, für welche Eigenschaft die Funktion aufgerufen wird?

Wenn Sie eine andere Herangehensweise haben würde ich es auch zu schätzen, aber in Anbetracht, dass ich alle NSUserDefaults Zeug in einer Klasse halten möchte, kann ich nicht an eine Alternative denken, die nicht 50 Getter und Setter schreiben.

Danke!

+0

50 Eigenschaften? klingt wie du solltest es in mehrere Klassen aufteilen. – vikingosegundo

Antwort

2

Ich fand Ihre Frage sehr interessant und ich sagte mir "Challenge accepted!".

Ich habe this Projekt auf Github erstellt.

Grundsätzlich alles, was Sie tun müssen, ist die VBSettings Klasse Unterklasse und dann de Eigenschaften erklären, wie folgt aus:

@interface MySettings : VBSettings 

@property (strong, nonatomic) NSString *hello; 

@end 

Der Wert von „Hallo“ wird mit der Taste „Hallo“ zu NSUserDefaults gespeichert werden. Anwendungsbeispiel:

MySettings settings = [[MySettings alloc] init]; 
settings.hello = "World!"; //The value is saved in NSUserDefaults 
NSLog(@"%@", settings.hello); //The value is restored from NSUserDefaults. 
+0

Vielen Dank dafür, es sieht gut aus und erfüllt genau den Zweck, den ich brauche.Ich möchte nur darauf hinweisen, dass dies für andere Leute sehr nützlich sein könnte. In diesem Fall könnten Sie vielleicht zwei weitere Methoden hinzufügen: clearAll und registerDefaults: @ {}. Ich beabsichtige definitiv beide in die Unterklasse zu implementieren, wenn der Benutzer sich abmeldet und standardmäßig in AppDelegate leicht sichtbar ist. Noch einmal Danke! –

+0

Danke! Ich schätze das Feedback. Ich bin wirklich bestrebt, das Projekt in naher Zukunft zu verbessern, einschließlich der beiden genannten Methoden. – vladiulianbogdan

+0

Und ich habe vergessen, "Challenge Completed!" ;) –

3

Der Setter und Getter in diesem Fall ist einfach, können Sie wie folgt tun:

- (void)setName:(NSString *)name { 
    [[NSUserDefaults standardUserDefaults] setObject:name forKey:@"name"]; 
    [[NSUserDefaults standardUserDefaults] synchronize]; 
} 

- (NSString *)name { 
    return [[NSUserDefaults standardUserDefaults] objectForKey:@"name"]; 
} 

Wenn Sie einen einfachen Ansatz für alle Eigenschaften verwenden möchten:

- (id)objectForKey:(NSString *)key { 
    return [[NSUserDefaults standardUserDefaults] objectForKey:key]; 
} 
- (void)setObject:(id)object forKey:(NSString *)key { 
    [[NSUserDefaults standardUserDefaults] setObject:object forKey:key]; 
    [[NSUserDefaults standardUserDefaults] synchronize]; 
} 

Statt viele schaffen Eigenschaften, erstellen Sie viele Schlüssel, jeder Schlüssel ist etwas, das Sie speichern oder abrufen möchten. Beispiel des Schlüssels:

static NSString *const kName = @"name"; 
static NSString *const kLastName = @"lastName"; 
+0

Sie haben Recht, das würde funktionieren und ich hatte tatsächlich etwas sehr ähnlich zu Ihrer ersten Version, aber das Problem ist, sobald Sie viele Eigenschaften haben, würden Sie 2x viele Funktionen haben, also wenn ich es aus irgendeinem Grund ändern müsste, wäre es ein Schmerz zu sein pflegen. Deshalb suche ich nach einer allgemeinen Option, so dass ein Getter und ein Setter für alle Eigenschaften arbeiten (etwas, was die Leute oben auf die eine oder andere Weise erreichen konnten). Schlüssel ist in der Tat eine andere Option, aber es ist nirgendwo so sauber, was mit der Menge an Code, die ich habe, ein Problem ist. Es ist jedoch definitiv ein praktikabler alternativer Ansatz. Vielen Dank! –

0

Eine Möglichkeit wäre, KVO zu verwenden, um festzustellen, wann Ihre Eigenschaften ändern.

z.:

@interface Settings : NSObject 

@property NSString *one; 
@property NSString *two; 

@end 

@implementation Settings 

- (instancetype)init 
{ 
    self = [super init]; 
    if (self) { 
     [self addObserver:self forKeyPath:@"one" options:NSKeyValueObservingOptionNew context:NULL]; 
     [self addObserver:self forKeyPath:@"two" options:NSKeyValueObservingOptionNew context:NULL]; 
    } 
    return self; 
} 

- (void)dealloc 
{ 
    [self removeObserver:self forKeyPath:@"one"]; 
    [self removeObserver:self forKeyPath:@"two"]; 
} 

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context 
{ 
    NSLog(@"Key: %@, Change: %@", keyPath, change); 
} 

@end 

In einer anderen Klasse, verwenden Sie die Standard-Eigenschaft Zugang:

Settings *settings = [[Settings alloc] init]; 
settings.one = @"something for one"; 

Die Einstellungen Objekt Protokolle:

Schlüssel: ein, Änderung: { Art = 1; new = "etwas für einen"; }

+0

Das ist ein schlauer Weg um das Problem, obwohl ich immer Probleme mit KVO hatte und es möglichst vermeiden möchte. –

0

Sie könnten versuchen, dynamische Getter und Setter Erklärungen as noted in this answer.

Erstellen Sie zunächst allgemeine Funktionen zu verwenden, die Sie alle nutzen die Eigenschaften wünschen:

- (id)_getter_ 
{ 
    return [[NSUserDefaults standardUserDefaults] objectForKey:NSStringFromSelector(_cmd)]; 
} 

- (void)_setter_:(id)value 
{ 
    //This one's _cmd name has "set" in it and an upper case first character 
    //This could take a little work to parse out the parameter name 
    [[NSUserDefaults standardUserDefaults] setObject:object forKey:YourParsedOutKey]; 
    [[NSUserDefaults standardUserDefaults] synchronize]; 
} 

dann die dynamische Methode Generator erstellen:

+(void)synthesizeForwarder:(NSString*)getterName 
{ 
    NSString*setterName=[NSString stringWithFormat:@"set%@%@:", 
      [[getterName substringToIndex:1] uppercaseString],[getterName substringFromIndex:1]]; 
    Method getter=class_getInstanceMethod(self, @selector(_getter_)); 
    class_addMethod(self, NSSelectorFromString(getterName), 
        method_getImplementation(getter), method_getTypeEncoding(getter)); 
    Method setter=class_getInstanceMethod(self, @selector(_setter_:)); 
    class_addMethod(self, NSSelectorFromString(setterName), 
        method_getImplementation(setter), method_getTypeEncoding(setter)); 
} 

Dann legen Sie fest, welche Strings Sie dynamische Getters und Sette erstellen möchten rs für:

+(void)load 
{ 
    for(NSString*selectorName in [NSArray arrayWithObjects:@"name", @"anything", @"else", @"you", @"want",nil]){ 
     [self synthesizeForwarder:selectorName]; 
    } 
} 

Das wird Getter und Setter für jeden Variablennamen erstellen, die Sie dem Array hinzufügen. Ich bin mir nicht sicher, wie gut das funktionieren wird, wenn andere Klassen versuchen, diese Methoden aufzurufen, der Compiler wird sie während der Kompilierung nicht sehen, so dass Fehler auftreten können, wenn Sie versuchen, sie zu verwenden. Ich habe gerade zwei weitere StackOverflow-Fragen in diese eine Antwort für Ihre Situation kombiniert.

Dynamic Getters and Setters.

Get current Method name.

+0

Sieht gut aus, aber ich mache mir Sorgen über die Kombination dieser beiden Konzepte. Es hat sicherlich das Potenzial zu arbeiten müsste getestet werden. Kudos, um darüber nachzudenken. –

0

Wie ich es verstehe, wollen Sie nicht den mentalen Aufwand für setObject:forKey: und objectForKey: Methodenaufrufe durch den Benutzer dieser Klasse.

Hier ist, wie Sie es umgehen können. Ich bin viel lückenlos für Sie ausfüllen

  1. die Eigenschaft in der Header-Datei deklarieren, so dass Anrufer sie verwenden können.

    @property NSString *something; 
    @property NSString *somethingElse; 
    
  2. in der Klassendatei selbst erklären dass Sie die Eigenschaften definieren, so dass der Compiler nicht aufregen bekommt:

    @dynamic something,somethingElse; 
    
  3. in der Klassendatei, implementieren die methodSignatureForSelector Funktion, wie folgt aus:

    -(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 
    {if (SelectorIsGetter(aSelector)) 
        return [NSMethodSignature signatureWithObjCTypes:"@@:"]; 
    if (SelectorIsSetter(aSelector)) 
        return [NSMethodSignature signatureWithObjCTypes:"[email protected]:@"]; 
    return nil; 
    } 
    

Dies wird das System anweisen forwardInvocation: für diese Wähler zu nennen, und es wird auch die Form des Gesprächs sagen, die gemacht wird.

Ihre Implementierung von SelectorIsGetter und SelectorIsSetter liegt bei Ihnen.Sie werden wahrscheinlich NSStringFromSelector(aSelector) verwenden, um den Namen des Selektors abzurufen, und dann in einer Namenstabelle nachsehen, ob er mit den Namen der von Ihnen implementierten Selektoren übereinstimmt: in diesem Fall something und somethingElse für die Getter und setSomething: und setSomethingElse: für die Setter.

  1. in der Klassendatei, implementieren die forwardInvocation: Funktion, wie folgt aus:

    -(void)forwardInvocation:(NSInvocation *)anInvocation 
    {if (SelectorIsGetter(anInvocation.selector)) 
        {NSString *s=[self objectForKey:NSStringFromSelector(anInvocation.selector)]; 
        [anInvocation setReturnValue:&s]; 
        return; 
        }; 
    if (SelectorIsSetter(anInvocation.selector)) 
        {NSString *s; 
        [anInvocation getArgument:&s atIndex:2]; 
        [self setObjectForKey:UnmangleName(NSStringFromSelector(anInvocation.selector))]; 
        return; 
        }; 
    [super forwardInvocation:anInvocation]; 
    } 
    

... wo UnmangleName eine durchaus mühsame Funktion ist, die wie einen String "setSomething:" und verwandelt es in eine Zeichenfolge wie "etwas".

Wenn Sie mehr als nur NSStrings tun möchten, ist die Erweiterung einigermaßen unkompliziert.

+0

Das hört sich nach einem vernünftigen Ansatz an, obwohl es offensichtlich ziemlich komplex ist, als die von vladiulianbogdan gebotene Klasse zu untergliedern. Trotzdem ist es eine gute Idee und ich danke Ihnen dafür, es hilft mir sicherlich, eine breitere Vorstellung von einer möglichen Umsetzung zu bekommen. –

2

Ein anderer Ansatz könnte dies sein. Keine Eigenschaften, nur Schlüsselwert tiefgestellt.

@interface DBObject : NSObject<NSCoding> 
+ (instancetype)sharedObject; 
@end 

@interface NSObject(SubScription) 
- (id)objectForKeyedSubscript:(id)key; 
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key; 
@end 

Auf der Implementierungsdatei:

+ (instancetype)sharedObject { 
    static DBObject *sharedObject = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
    sharedObject = [[DBObject alloc] init]; 
    }); 
    return sharedObject; 
} 

- (id)objectForKeyedSubscript:(id)key { 
    return [[NSUserDefaults standardUserDefaults] objectForKey:key]; 
} 

- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key { 
    [[NSUserDefaults standardUserDefaults] setObject:obj forKeyedSubscript:key]; 
    [[NSUserDefaults standardUserDefaults] synchronize]; 
} 

Jetzt, können Sie es wie folgt verwenden können:

// You saved it in NSUserDefaults 
[DBObject sharedObject][@"name"] = @"John"; 

// You retrieve it from NSUserDefaults 
NSLog(@"Name is: %@", [DBObject sharedObject][@"name"]); 

ich das dies der beste Ansatz ist, und ist das, was ich in die verwenden Zukunft.

+0

Sorry Herald für die späte Antwort, ich habe nur deine Antwort bemerkt. Sie sind ziemlich richtig, dass dies funktionieren würde, aber es verbessert nicht viel auf Standard NSUserDefaults. Ich muss immer noch ein gemeinsames Objekt (wie StandardDefaults) bekommen, und muss immer noch einen Schlüssel zur Verfügung stellen, der die Rechtschreibung nicht prüft. Eventuell [[NSUserDefaults standardDefaults] setObject: "John" forKey: "name"]; unterscheidet sich nicht sehr von [DBObject sharedObject] [@ "name"] = @ "John"; Es ist ein bisschen kürzer und sieht schöner aus, also stimme ich zu, dass es eine Verbesserung ist, gerade nicht genug, um es wert zu sein, alle Code-Präferenzen neu zu schreiben. –