wake-up-neo.com

Liefert den Namen der Eigenschaft als Zeichenfolge

Ich brauche einen Weg, um eine Eigenschaft zu übergeben und den Namen vergeben zu bekommen. Irgendwelche Vorschläge?

@property (nonatomic, retain) MyObject *crazyObject;

NSString *str = SOME_WAY_TO_GET_PROPERTY_NAME(crazyObject);
// Above method should return @"crazyObject"
24
aryaxt

Sie können dies versuchen:

unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList([self class], &propertyCount);

NSMutableArray * propertyNames = [NSMutableArray array];
for (unsigned int i = 0; i < propertyCount; ++i) {
  objc_property_t property = properties[i];
  const char * name = property_getName(property);
  [propertyNames addObject:[NSString stringWithUTF8String:name]];
}
free(properties);
NSLog(@"Names: %@", propertyNames);
23
Oscar Gomez

Es ist so einfach wie das ... und erweitert das, was Chuck bereits erwähnt hat:

#ifndef STR_PROP
    #define STR_PROP( prop ) NSStringFromSelector(@selector(prop))
#endif

Sie verwenden es dann so:

NSString *strProp = STR_PROP(myProperty);
20
Arvin

Hintergrund

Denken Sie daran, dass Eigenschaften wirklich nur, um zitieren Sie Apple "eine syntaktische Abkürzung für die Deklaration der Zugriffsmethoden einer Klasse" sind. In der Tat funktioniert die @property-Deklaration selbst nicht. Ihre @synthesize-Anweisung übersetzt den @property in zwei Methoden:

- (void)setCrazyObject:(MyObject *)something;
- (MyObject *)crazyObject;

Welcher verwendet wird, hängt vom Kontext Ihres self.crazyObject ab. (@synthesize erstellt auch eine übereinstimmende Instanzvariable, wenn Sie es nicht selbst getan haben.) Der Ableger von all dem ist, dass Sie nicht wirklich mit einer einzigen Methode in eine Eigenschaft und aus einer Eigenschaft übersetzen können.

Lösungsvorschlag

Sie können das nutzen, was Apple bereits bietet:

NSString *foo = NSStringFromSelector(@selector(myClassProperty));

Oder mache etwas Brauches:

Da self.crazyObject bei Ausführung des Codes tatsächlich in [self crazyObject] oder [self setCrazyObject:foo] übersetzt wird, werden Sie wahrscheinlich zwei Methoden benötigen:

- (NSString *)setterStringForProperty:(SEL)prop;
- (NSString *)getterStringForProperty:(SEL)prop;

Sie möchten dann mindestens zwei Begleitmethoden wie:

- (SEL)setterForPropertyName:(NSString *)propString;
- (SEL)getterForPropertyName:(NSString *)propString;

Innerhalb dieser Methoden können Sie die Foundation-FunktionenNSStringFromSelector und NSSelectorFromString verwenden, um zwischen SEL und NSString hin und her zu konvertieren. Verwenden Sie beliebige Zeichenfolgenmanipulationen, die Sie zwischen Ihrer Setterzeichenfolge (setCrazyObject) und Ihrem Eigenschaftennamen (crazyObject) hin und her konvertieren möchten.

Es ist schwierig, eine vollständige Lösung bereitzustellen, ohne den genauen Anwendungsfall zu kennen, aber hoffentlich liefert dies weitere Hinweise für jeden, der etwas Ähnliches zu erreichen versucht. Möglicherweise sind sogar einige nützliche Dinge möglich, wenn dieser Ansatz mit Oscars Antwort kombiniert wird.

14
clozach

Hier ist eine Funktion, die den Namen eines ivar zurückgibt, also im Grunde nicht nur die Eigenschaften, sondern auch jedes ivar der Klasse. Ich habe keinen Weg gefunden, die Immobilie direkt zu beziehen, also habe ich den ivar-Trick benutzt.

#import <objc/objc.h>

/// -----

- (NSString *)nameOfIvar:(id)ivarPtr
{
    NSString *name = nil;

    uint32_t ivarCount;
    Ivar *ivars = class_copyIvarList([self class], &ivarCount);

    if(ivars)
    {
        for(uint32_t i=0; i<ivarCount; i++)
        {
            Ivar ivar = ivars[i];

            id pointer = object_getIvar(self, ivar);
            if(pointer == ivarPtr)
            {
                name = [NSString stringWithUTF8String:ivar_getName(ivar)];            
                break;
            }
        }

        free(ivars);
    }

    return name;
}
4
JustSid

Nach dem Suchen und Debuggen finde ich eine Lösung für mich ...

#import <objc/runtime.h> hinzugefügt

Die Methoden object_getIvar (id obj, Ivar ivar) senden einen fehlerhaften Zugriff und App-Abstürze. Ich modifiziere etwas Code und es hat super funktioniert:

+(NSString*)stringWithProperty:(id)property withClass:(id)controller
{
    NSString *name = nil;

    uint32_t ivarCount;

    Ivar *ivars = class_copyIvarList([controller class], &ivarCount);

    if(ivars)
    {
        for(uint32_t i=0; i<ivarCount; i++)
        {
            Ivar ivar = ivars[i];

            name = [NSString stringWithUTF8String:ivar_getName(ivar)];

            if ([controller valueForKey:name] == property)
            {
                break;
            }
        }

        free(ivars);
    }

    return name;
}
3

Wenn Sie die Lösung ändern, funktioniert sie, wenn Ihr Objekt bereits zugewiesen ist. Andernfalls wird nil zurückgegeben: -

NSString * NSStringFromProperty(NSObject* property, NSObject* class)
{
    unsigned int propertyCount = 0;
    objc_property_t * properties = class_copyPropertyList([class class], &propertyCount);

    NSString *name = nil;
    for (unsigned int i = 0; i < propertyCount; ++i)
    {
        name = [NSString stringWithUTF8String:property_getName(properties[i])];

        NSObject *object = [class valueForKey:name];

        if (object != nil && object == property)
        {
            break;
        }
        else
        {
            name = nil;
        }
    }
    free(properties);

    return name;
}

Von Eigenschaftsnamen als String abrufen, ohne die Laufzeitbibliothek zu verwenden , einfach definieren:

#define propertyKeyPath(property) (@""#property)
#define propertyKeyPathLastComponent(property) [[(@""#property) componentsSeparatedByString:@"."] lastObject]

Und dann kannst du so etwas machen:

 NSLog(@"%@", propertyKeyPathLastComponent(appleStore.storeLocation.street)); //result: street
1
samthui7

Sie können meinen Ansatz unter Gist überprüfen, um die Zeichenfolge für eine Eigenschaft mit automatischer Vervollständigung und Überprüfung der Kompilierungszeit zu erhalten. 

Wie benutzt man:

Rufen Sie den Eigenschaftsnamen für eine Klasse ab:

@interface AnyClass : NSObject
@property (strong) NSData *data;
@end

// == My approach ==
// C string for a class
PropertyNameForClass(AnyClass, data);    // ==> "data"
// NSString for a class
PropertyStringForClass(AnyClass, data);  // ==> @"data"

// Bad approach (no autocompletion; no compile-time check):
NSString *propertyName = @"data";

Rufen Sie den Eigenschaftsnamen für ein Protokoll ab:

@protocol AnyProtocol
@property (strong) NSDate *date;
@end

// C string for a protocol
PropertyNameForProtocol(AnyProtocol, date);    // ==> "date"
// NSString for a protocol
PropertyStringForProtocol(AnyProtocol, date);  // ==> @"date"
1
李岡諭

Sie können verwenden 

NSString *str = NSStringFromSelector(@selector(crazyObject));

Das Gute an diesem Ansatz ist:

  1. Xcode wird automatisch das Wort crazyObject für Sie automatisch vervollständigen.
  2. Wenn Sie später den Eigenschaftsnamen von crazyObject in myCrazyObject ändern, fügt Xcode eine Warnung hinzu, die besagt, dass "unrecognized selector!" - ziemlich gut für das Debuggen geeignet ist.

Ich verwende diese Methode so oft, dass ich sogar eine Funktion erstellt habe, mit der weniger Buchstaben geschrieben werden können:

NSString * __nonnull sfs(SEL __nonnull theSelector)
{
    if (!theSelector)
    {
        abort();
    }
    return NSStringFromSelector(theSelector);
}

Nun kann Ihre endgültige Lösung so aussehen:

NSString *str = sfs(@selector(crazyObject));
1
OlDor

Unkonventionell, hackig, hässlich, spät, aber ... so stark wie es nur geht und wie ein Zauber wirkt:

#define SOME_WAY_TO_GET_PROPERTY_NAME(p) p == p ? [[[[[[[NSString alloc] initWithCString:#p encoding:NSUTF8StringEncoding] componentsSeparatedByString:@"."] lastObject] componentsSeparatedByString:@" "] lastObject] stringByReplacingOccurrencesOfString:@"]" withString:@""] : @""

Verwendungsbeispiel:

NSLog(SOME_WAY_TO_GET_PROPERTY_NAME(self.customer.surname));
NSLog(SOME_WAY_TO_GET_PROPERTY_NAME([[self customer] birthDate]));
...
0
manta