Ruft eine Objekteigenschaftenliste in Objective-C ab

109

Wie kann ich in Objective-C eine Liste (in Form eines NSArrayoder NSDictionary) bestimmter Objekteigenschaften abrufen?

Stellen Sie sich das folgende Szenario vor: Ich habe eine übergeordnete Klasse definiert, die nur erweitert wird NSObjectund ein NSString, ein BOOLund ein NSDataObjekt als Eigenschaften enthält. Dann habe ich mehrere Klassen, die diese übergeordnete Klasse erweitern und jeweils viele verschiedene Eigenschaften hinzufügen.

Gibt es eine Möglichkeit, eine Instanzmethode für die übergeordnete Klasse zu implementieren , die das gesamte Objekt durchläuft und beispielsweise eine NSArrayder (untergeordneten) Klasseneigenschaften zurückgibt, da NSStringsdiese nicht für die übergeordnete Klasse gelten, sodass ich diese später verwenden kann? NSStringfür KVC?

Boliva
quelle

Antworten:

116

Ich habe es gerade geschafft, die Antwort selbst zu bekommen. Durch die Verwendung der Obj-C-Laufzeitbibliothek hatte ich Zugriff auf die Eigenschaften, wie ich wollte:

- (void)myMethod {
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList([self class], &outCount);
    for(i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        const char *propName = property_getName(property);
        if(propName) {
            const char *propType = getPropertyType(property);
            NSString *propertyName = [NSString stringWithCString:propName
                                                                encoding:[NSString defaultCStringEncoding]];
            NSString *propertyType = [NSString stringWithCString:propType
                                                                encoding:[NSString defaultCStringEncoding]];
            ...
        }
    }
    free(properties);
}

Dazu musste ich eine C-Funktion 'getPropertyType' erstellen, die hauptsächlich aus einem Apple-Codebeispiel stammt (ich kann mich derzeit nicht an die genaue Quelle erinnern):

static const char *getPropertyType(objc_property_t property) {
    const char *attributes = property_getAttributes(property);
    char buffer[1 + strlen(attributes)];
    strcpy(buffer, attributes);
    char *state = buffer, *attribute;
    while ((attribute = strsep(&state, ",")) != NULL) {
        if (attribute[0] == 'T') {
            if (strlen(attribute) <= 4) {
                break;
            }
            return (const char *)[[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];
        }
    }
    return "@";
}
Boliva
quelle
5
+1, außer dass dies bei Grundelementen wie int fehlschlägt. In meiner Antwort unten finden Sie eine leicht verbesserte Version derselben Sache.
Jpswain
1
Aus Gründen der Richtigkeit [NSString stringWithCString:]wird zugunsten von abgelehnt [NSString stringWithCString:encoding:].
Zekel
4
Sollte den objc-Laufzeitheader #import <objc / runtime.h> importieren. Es funktioniert auf ARC.
Dae KIM
Hier erfahren Sie, wie Sie dies mit Swift erreichen.
Ramis
76

Die Antwort von @ boliva ist gut, benötigt aber ein wenig mehr, um mit Grundelementen wie int, long, float, double usw. umgehen zu können.

Ich habe aus seiner aufgebaut, um diese Funktionalität hinzuzufügen.

// PropertyUtil.h
#import 

@interface PropertyUtil : NSObject

+ (NSDictionary *)classPropsFor:(Class)klass;

@end


// PropertyUtil.m
#import "PropertyUtil.h"
#import "objc/runtime.h"

@implementation PropertyUtil

static const char * getPropertyType(objc_property_t property) {
    const char *attributes = property_getAttributes(property);
    printf("attributes=%s\n", attributes);
    char buffer[1 + strlen(attributes)];
    strcpy(buffer, attributes);
    char *state = buffer, *attribute;
    while ((attribute = strsep(&state, ",")) != NULL) {
        if (attribute[0] == 'T' && attribute[1] != '@') {
            // it's a C primitive type:
            /* 
                if you want a list of what will be returned for these primitives, search online for
                "objective-c" "Property Attribute Description Examples"
                apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc.            
            */
            return (const char *)[[NSData dataWithBytes:(attribute + 1) length:strlen(attribute) - 1] bytes];
        }        
        else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {
            // it's an ObjC id type:
            return "id";
        }
        else if (attribute[0] == 'T' && attribute[1] == '@') {
            // it's another ObjC object type:
            return (const char *)[[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];
        }
    }
    return "";
}


+ (NSDictionary *)classPropsFor:(Class)klass
{    
    if (klass == NULL) {
        return nil;
    }

    NSMutableDictionary *results = [[[NSMutableDictionary alloc] init] autorelease];

    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList(klass, &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        const char *propName = property_getName(property);
        if(propName) {
            const char *propType = getPropertyType(property);
            NSString *propertyName = [NSString stringWithUTF8String:propName];
            NSString *propertyType = [NSString stringWithUTF8String:propType];
            [results setObject:propertyType forKey:propertyName];
        }
    }
    free(properties);

    // returning a copy here to make sure the dictionary is immutable
    return [NSDictionary dictionaryWithDictionary:results];
}




@end
jpswain
quelle
1
#import <Foundation/Foundation.h>Wollten Sie oben in der .h-Datei haben?
Andrew
2
[NSString stringWithUTF8String: propType] konnte "propType const char *" NSNumber \ x94 \ xfdk; "nicht analysieren und gibt einen Null-String zurück ... Weiß nicht, warum es so eine seltsame NSNumber ist. Mb weil
ActiveRecord
Hervorragend! Vielen Dank.
Azik Abdullah
Das ist absolut perfekt!
Pranoy C
28

Die Antwort von @ orange80 hat ein Problem: Die Zeichenfolge wird nicht immer mit 0s beendet. Dies kann zu unerwarteten Ergebnissen wie Abstürzen führen, während versucht wird, es in UTF8 zu konvertieren (ich hatte tatsächlich nur deswegen einen ziemlich nervigen Absturzfehler. Es hat Spaß gemacht, ihn zu debuggen ^^). Ich habe es behoben, indem ich tatsächlich einen NSString aus dem Attribut abgerufen und dann cStringUsingEncoding: aufgerufen habe. Das funktioniert jetzt wie ein Zauber. (Funktioniert auch mit ARC, zumindest für mich)

Das ist also jetzt meine Version des Codes:

// PropertyUtil.h
#import 

@interface PropertyUtil : NSObject

+ (NSDictionary *)classPropsFor:(Class)klass;

@end


// PropertyUtil.m
#import "PropertyUtil.h"
#import <objc/runtime.h>

@implementation PropertyUtil

static const char *getPropertyType(objc_property_t property) {
    const char *attributes = property_getAttributes(property);
    //printf("attributes=%s\n", attributes);
    char buffer[1 + strlen(attributes)];
    strcpy(buffer, attributes);
    char *state = buffer, *attribute;
    while ((attribute = strsep(&state, ",")) != NULL) {
        if (attribute[0] == 'T' && attribute[1] != '@') {
            // it's a C primitive type:
            /*
             if you want a list of what will be returned for these primitives, search online for
             "objective-c" "Property Attribute Description Examples"
             apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc.
             */
            NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];
            return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
        }
        else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {
            // it's an ObjC id type:
            return "id";
        }
        else if (attribute[0] == 'T' && attribute[1] == '@') {
            // it's another ObjC object type:
            NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding];
            return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
        }
    }
    return "";
}


+ (NSDictionary *)classPropsFor:(Class)klass
{
    if (klass == NULL) {
        return nil;
    }

    NSMutableDictionary *results = [[NSMutableDictionary alloc] init];

    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList(klass, &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        const char *propName = property_getName(property);
        if(propName) {
            const char *propType = getPropertyType(property);
            NSString *propertyName = [NSString stringWithUTF8String:propName];
            NSString *propertyType = [NSString stringWithUTF8String:propType];
            [results setObject:propertyType forKey:propertyName];
        }
    }
    free(properties);

    // returning a copy here to make sure the dictionary is immutable
    return [NSDictionary dictionaryWithDictionary:results];
}

@end
Felinira
quelle
@farthen können Sie ein Beispiel angeben, das das Problem mit dem von mir bereitgestellten Code demonstriert? Ich bin nur neugierig, es zu sehen.
Jpswain
@ orange80 Nun, AFAIR die Daten werden nie nullterminiert. Wenn dies der Fall ist, geschieht dies nur durch einen Unfall. Ich kann mich jedoch irren. In anderen Nachrichten: Ich habe immer noch diesen Code ausgeführt und er läuft absolut solide: p
absolut felinira
@ orange80 Ich bin auf dieses Problem gestoßen, als ich versucht habe, Ihre Version auf IMAAdRequest aus der IMA-Anzeigenbibliothek von Google aufzurufen. Furzens Lösung löste es.
Christopher Pickslay
Vielen Dank. Dies funktionierte für mich in iOS7, als die beiden vorherigen Antworten dies nicht taten. +1 für alle 3.
ChrisH
Dies ist die einzige Antwort, die für mich funktioniert hat. Alles andere gab mir wie "NSString \ x8d \ xc0 \ xd9" Verrücktheit für die Eigenschaftstypen, vermutlich weil die Charakterisierung nicht stimmte
Brian Colavito
8

Als ich es mit iOS 3.2 versuchte, funktionierte die Funktion getPropertyType nicht gut mit der Eigenschaftsbeschreibung. Ich habe ein Beispiel aus der iOS-Dokumentation gefunden: "Objective-C Runtime Programming Guide: Deklarierte Eigenschaften".

Hier ist ein überarbeiteter Code für die Auflistung von Eigenschaften in iOS 3.2:

#import <objc/runtime.h>
#import <Foundation/Foundation.h>
...
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([UITouch class], &outCount);
for(i = 0; i < outCount; i++) {
    objc_property_t property = properties[i];
    fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
free(properties);
Chatchavan
quelle
7

Ich habe festgestellt, dass die Lösung von Boliva im Simulator einwandfrei funktioniert, aber auf dem Gerät verursacht der Teilstring mit fester Länge Probleme. Ich habe eine objektiv-C-freundlichere Lösung für dieses Problem geschrieben, die auf dem Gerät funktioniert. In meiner Version konvertiere ich den C-String der Attribute in einen NSString und führe String-Operationen aus, um einen Teilstring mit nur der Typbeschreibung zu erhalten.

/*
 * @returns A string describing the type of the property
*/

+ (NSString *)propertyTypeStringOfProperty:(objc_property_t) property {
    const char *attr = property_getAttributes(property);
    NSString *const attributes = [NSString stringWithCString:attr encoding:NSUTF8StringEncoding];

    NSRange const typeRangeStart = [attributes rangeOfString:@"T@\""];  // start of type string
    if (typeRangeStart.location != NSNotFound) {
        NSString *const typeStringWithQuote = [attributes substringFromIndex:typeRangeStart.location + typeRangeStart.length];
        NSRange const typeRangeEnd = [typeStringWithQuote rangeOfString:@"\""]; // end of type string
        if (typeRangeEnd.location != NSNotFound) {
            NSString *const typeString = [typeStringWithQuote substringToIndex:typeRangeEnd.location];
            return typeString;
        }
    }
    return nil;
}

/**
* @returns (NSString) Dictionary of property name --> type
*/

+ (NSDictionary *)propertyTypeDictionaryOfClass:(Class)klass {
    NSMutableDictionary *propertyMap = [NSMutableDictionary dictionary];
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList(klass, &outCount);
    for(i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        const char *propName = property_getName(property);
        if(propName) {

            NSString *propertyName = [NSString stringWithCString:propName encoding:NSUTF8StringEncoding];
            NSString *propertyType = [self propertyTypeStringOfProperty:property];
            [propertyMap setValue:propertyType forKey:propertyName];
        }
    }
    free(properties);
    return propertyMap;
}
Mitchell Vanderhoeff
quelle
Dies löst eine EXC_BAD_ACCESS-Ausnahme auf NSRange const typeRangeStart = [Attribute rangeOfString: @ "T @ \" "]; // Beginn der Typzeichenfolge
Adam Mendoza
6

Diese Implementierung funktioniert sowohl mit Objective-C-Objekttypen als auch mit C-Grundelementen. Es ist iOS 8-kompatibel. Diese Klasse bietet drei Klassenmethoden:

+ (NSDictionary *) propertiesOfObject:(id)object;

Gibt ein Wörterbuch aller sichtbaren Eigenschaften eines Objekts zurück, einschließlich der Eigenschaften aller seiner Oberklassen.

+ (NSDictionary *) propertiesOfClass:(Class)class;

Gibt ein Wörterbuch aller sichtbaren Eigenschaften einer Klasse zurück, einschließlich der Eigenschaften aller ihrer Oberklassen.

+ (NSDictionary *) propertiesOfSubclass:(Class)class;

Gibt ein Wörterbuch aller sichtbaren Eigenschaften zurück, die für eine Unterklasse spezifisch sind . Eigenschaften für seine Oberklassen sind nicht enthalten.

Ein nützliches Beispiel für die Verwendung dieser Methoden ist das Kopieren eines Objekts in eine Unterklasseninstanz in Objective-C, ohne dass die Eigenschaften in einer Kopiermethode angegeben werden müssen . Teile dieser Antwort basieren auf den anderen Antworten auf diese Frage, bieten jedoch eine übersichtlichere Schnittstelle zu den gewünschten Funktionen.

Header:

//  SYNUtilities.h

#import <Foundation/Foundation.h>

@interface SYNUtilities : NSObject
+ (NSDictionary *) propertiesOfObject:(id)object;
+ (NSDictionary *) propertiesOfClass:(Class)class;
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
@end

Implementierung:

//  SYNUtilities.m

#import "SYNUtilities.h"
#import <objc/objc-runtime.h>

@implementation SYNUtilities
+ (NSDictionary *) propertiesOfObject:(id)object
{
    Class class = [object class];
    return [self propertiesOfClass:class];
}

+ (NSDictionary *) propertiesOfClass:(Class)class
{
    NSMutableDictionary * properties = [NSMutableDictionary dictionary];
    [self propertiesForHierarchyOfClass:class onDictionary:properties];
    return [NSDictionary dictionaryWithDictionary:properties];
}

+ (NSDictionary *) propertiesOfSubclass:(Class)class
{
    if (class == NULL) {
        return nil;
    }

    NSMutableDictionary *properties = [NSMutableDictionary dictionary];
    return [self propertiesForSubclass:class onDictionary:properties];
}

+ (NSMutableDictionary *)propertiesForHierarchyOfClass:(Class)class onDictionary:(NSMutableDictionary *)properties
{
    if (class == NULL) {
        return nil;
    }

    if (class == [NSObject class]) {
        // On reaching the NSObject base class, return all properties collected.
        return properties;
    }

    // Collect properties from the current class.
    [self propertiesForSubclass:class onDictionary:properties];

    // Collect properties from the superclass.
    return [self propertiesForHierarchyOfClass:[class superclass] onDictionary:properties];
}

+ (NSMutableDictionary *) propertiesForSubclass:(Class)class onDictionary:(NSMutableDictionary *)properties
{
    unsigned int outCount, i;
    objc_property_t *objcProperties = class_copyPropertyList(class, &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = objcProperties[i];
        const char *propName = property_getName(property);
        if(propName) {
            const char *propType = getPropertyType(property);
            NSString *propertyName = [NSString stringWithUTF8String:propName];
            NSString *propertyType = [NSString stringWithUTF8String:propType];
            [properties setObject:propertyType forKey:propertyName];
        }
    }
    free(objcProperties);

    return properties;
}

static const char *getPropertyType(objc_property_t property) {
    const char *attributes = property_getAttributes(property);
    char buffer[1 + strlen(attributes)];
    strcpy(buffer, attributes);
    char *state = buffer, *attribute;
    while ((attribute = strsep(&state, ",")) != NULL) {
        if (attribute[0] == 'T' && attribute[1] != '@') {
            // A C primitive type:
            /*
             For example, int "i", long "l", unsigned "I", struct.
             Apple docs list plenty of examples of values returned. For a list
             of what will be returned for these primitives, search online for
             "Objective-c" "Property Attribute Description Examples"
             */
            NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];
            return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
        }
        else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {
            // An Objective C id type:
            return "id";
        }
        else if (attribute[0] == 'T' && attribute[1] == '@') {
            // Another Objective C id type:
            NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding];
            return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
        }
    }
    return "";
}

@end
Duncan Babbage
quelle
Ich erhalte eine EXC_BAD_ACCESS-Ausnahme in dieser Zeile. NSString * name = [[NSString-Zuweisung] initWithBytes: Attribut + 1 Länge: strlen (Attribut) - 1 Codierung: NSASCIIStringEncoding];
Adam Mendoza
4

Wenn jemand auch die von den übergeordneten Klassen geerbten Eigenschaften abrufen muss (wie ich), werden hier einige Änderungen am " orange80 " -Code vorgenommen, um ihn rekursiv zu machen:

+ (NSDictionary *)classPropsForClassHierarchy:(Class)klass onDictionary:(NSMutableDictionary *)results
{
    if (klass == NULL) {
        return nil;
    }

    //stop if we reach the NSObject class as is the base class
    if (klass == [NSObject class]) {
        return [NSDictionary dictionaryWithDictionary:results];
    }
    else{

        unsigned int outCount, i;
        objc_property_t *properties = class_copyPropertyList(klass, &outCount);
        for (i = 0; i < outCount; i++) {
            objc_property_t property = properties[i];
            const char *propName = property_getName(property);
            if(propName) {
                const char *propType = getPropertyType(property);
                NSString *propertyName = [NSString stringWithUTF8String:propName];
                NSString *propertyType = [NSString stringWithUTF8String:propType];
                [results setObject:propertyType forKey:propertyName];
            }
        }
        free(properties);

        //go for the superclass
        return [PropertyUtil classPropsForClassHierarchy:[klass superclass] onDictionary:results];

    }
}
PakitoV
quelle
1
Könnten wir dies nicht zu einer Kategorie machen und NSObject damit erweitern, sodass diese Funktionalität in jede Klasse integriert ist, die ein untergeordnetes Element von NSObject ist?
Alex Zavatone
Das klingt nach einer guten Idee, wenn ich die Zeit finde, wird die Antwort mit dieser Option aktualisiert.
PakitoV
Sobald Sie damit fertig sind, füge ich einen Methoden-Dump hinzu, wenn ich Zeit habe. Es ist an der Zeit, dass wir über jedes NSObject eine echte Objekteigenschaft und Methodenintrospektion erhalten.
Alex Zavatone
Ich habe auch daran gearbeitet, eine Wertausgabe zu erzielen, aber es scheint, dass für einige Strukturen (rects) der Typ der tatsächliche Wert der Eigenschaft ist. Dies ist der Fall, wenn caretRect eines tableViewController und andere vorzeichenlose Ints in einer viewController-Struktur c oder f als den Typ zurückgeben, der mit den Runtime-Dokumenten für Objective-C in Konflikt steht. Hier ist deutlich mehr Arbeit erforderlich, um dies zu vervollständigen. developer.apple.com/library/mac/documentation/cocoa/conceptual/…
Alex Zavatone
Ich habe einen Blick darauf geworfen, aber es gibt ein Problem, das ich nicht umgehen kann. Um es rekursiv zu machen, muss ich die Methode für die Oberklasse aufrufen (wie in der letzten Zeile des vorherigen Codes), da NSObject die Stammklasse ist, die innerhalb einer Kategorie nicht funktioniert . Also keine Rekursivität möglich ... :( Ich bin mir nicht sicher, ob eine Kategorie in NSObject der richtige Weg ist ...
PakitoV
3

Das Wort "Attribute" ist etwas verschwommen. Meinen Sie Instanzvariablen, Eigenschaften, Methoden, die wie Accessoren aussehen?

Die Antwort auf alle drei lautet "Ja, aber es ist nicht sehr einfach." Die Objective-C-Laufzeit-API enthält Funktionen zum Abrufen der ivar-Liste, Methodenliste oder Eigenschaftsliste für eine Klasse (z. B. class_copyPropertyList()) und anschließend eine entsprechende Funktion für jeden Typ, um den Namen eines Elements in der Liste (z property_getName(). B. ) abzurufen .

Alles in allem kann es eine Menge Arbeit sein, es richtig zu machen, oder zumindest viel mehr, als die meisten Leute für das tun möchten, was normalerweise eine wirklich triviale Funktion ist.

Alternativ können Sie auch ein Ruby / Python-Skript schreiben, das nur eine Header-Datei liest und nach "Attributen" für die Klasse sucht.

Futter
quelle
Hallo Chuck, danke für deine Antwort. Was ich mit 'Attributen' meinte, war in der Tat eine Klasseneigenschaft. Mit der Obj-C Runtime Library konnte ich bereits das erreichen, was ich wollte. Die Verwendung eines Skripts zum Parsen der Header-Datei hätte für die Laufzeit nicht funktioniert.
Boliva
3

Ich konnte die Antwort von @ orange80 erhalten, um mit ARC ENABLED zu arbeiten ... ... für das, was ich wollte - zumindest ... aber nicht ohne ein bisschen Versuch und Irrtum. Hoffentlich ersparen diese zusätzlichen Informationen jemandem den Kummer.

Speichern Sie die Klassen, die er in seiner Antwort = als Klasse beschreibt, und setzen Sie sie in Ihre AppDelegate.h(oder was auch immer) #import PropertyUtil.h. Dann in Ihrem ...

- (void)applicationDidFinishLaunching:
         (NSNotification *)aNotification {

Methode (oder was auch immer)

PropertyUtil *props  = [PropertyUtil new];  
NSDictionary *propsD = [PropertyUtil classPropsFor:
                          (NSObject*)[gist class]];  
NSLog(@"%@, %@", props, propsD);

Das Geheimnis ist die Instanz - Variable Ihrer Klasse (werfen in diesem Fall meine Klasse ist Gist, und meine Instanz Gististgist ) , die Sie abfragen wollen ... zu NSObject ... (id), etc, wird es nicht .. schneiden für verschiedene, seltsame esoterische Gründe. Dies gibt Ihnen eine Ausgabe wie folgt ...

<PropertyUtil: 0x7ff0ea92fd90>, {
apiURL = NSURL;
createdAt = NSDate;
files = NSArray;
gistDescription = NSString;
gistId = NSString;
gitPullURL = NSURL;
gitPushURL = NSURL;
htmlURL = NSURL;
isFork = c;
isPublic = c;
numberOfComments = Q;
updatedAt = NSDate;
userLogin = NSString;
}

Für all Apples unverfrorenes / OCD, das mit ObjCs "Amazeballs" "Selbstbeobachtung prahlt ... Sie machen es sicher nicht sehr einfach, diesen einfachen" Blick "" auf sich selbst "," sozusagen ", auszuführen.

Wenn du aber wirklich wild werden willst, dann schau dir das an. Class-Dump , das ist eine unglaublich verrückte Art, in Klassen-Header von JEDER ausführbaren Datei usw. zu schauen. Es bietet einen VERBOSE Blick in deine Klassen. persönlich finden wirklich hilfreich - unter vielen, vielen Umständen. Aus diesem Grund habe ich nach einer Lösung für die Frage des OP gesucht. Hier sind einige der Verwendungsparameter. Viel Spaß!

    -a             show instance variable offsets
    -A             show implementation addresses
    --arch <arch>  choose a specific architecture from a universal binary (ppc, ppc64, i386, x86_64)
    -C <regex>     only display classes matching regular expression
    -f <str>       find string in method name
    -I             sort classes, categories, and protocols by inheritance (overrides -s)
    -r             recursively expand frameworks and fixed VM shared libraries
    -s             sort classes and categories by name
    -S             sort methods by name
Alex Gray
quelle
3

Du hast drei Zaubersprüche

Ivar* ivars = class_copyIvarList(clazz, &count); // to get all iVars
objc_property_t  *properties = class_copyPropertyList(clazz, &count); //to get all properties of a class 
Method* methods = class_copyMethodList(clazz, &count); // to get all methods of a class.

Der folgende Code kann Ihnen helfen.

-(void) displayClassInfo
{
    Class clazz = [self class];
    u_int count;

    Ivar* ivars = class_copyIvarList(clazz, &count);
    NSMutableArray* ivarArray = [NSMutableArray arrayWithCapacity:count];
    for (int i = 0; i < count ; i++)
    {
        const char* ivarName = ivar_getName(ivars[i]);
        ivarArray addObject:[NSString  stringWithCString:ivarName encoding:NSUTF8StringEncoding]];
    }
    free(ivars);

    objc_property_t* properties = class_copyPropertyList(clazz, &count);
    NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count];
    for (int i = 0; i < count ; i++)
    {
        const char* propertyName = property_getName(properties[i]);
        [propertyArray addObject:[NSString  stringWithCString:propertyName encoding:NSUTF8StringEncoding]];
    }
    free(properties);

    Method* methods = class_copyMethodList(clazz, &count);
    NSMutableArray* methodArray = [NSMutableArray arrayWithCapacity:count];
    for (int i = 0; i < count ; i++)
    {
        SEL selector = method_getName(methods[i]);
        const char* methodName = sel_getName(selector);
        [methodArray addObject:[NSString  stringWithCString:methodName encoding:NSUTF8StringEncoding]];
    }
    free(methods);

    NSDictionary* classInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                           ivarArray, @"ivars",
                           propertyArray, @"properties",
                           methodArray, @"methods",
                           nil];

        NSLog(@"%@", classInfo);
}
Ans
quelle
2

Ich habe die bereitgestellte Funktion boliva verwendet, aber anscheinend funktioniert sie nicht mehr mit iOS 7. Anstelle der statischen Eigenschaft const char * getPropertyType (Eigenschaft objc_property_t) kann jetzt nur Folgendes verwendet werden:

- (NSString*) classOfProperty:(NSString*)propName{

objc_property_t prop = class_getProperty([self class], [propName UTF8String]);
if (!prop) {
    // doesn't exist for object
    return nil;
}
const char * propAttr = property_getAttributes(prop);
NSString *propString = [NSString stringWithUTF8String:propAttr];
NSArray *attrArray = [propString componentsSeparatedByString:@","];
NSString *class=[attrArray objectAtIndex:0];
return [[class stringByReplacingOccurrencesOfString:@"\"" withString:@""] stringByReplacingOccurrencesOfString:@"T@" withString:@""];
}
Andrey Finayev
quelle
Du bist mein Held. Ich muss noch einige Dinge manuell korrigieren (aus irgendeinem Grund werden BOOLs als 'Tc' angezeigt), aber dies hat mir tatsächlich ermöglicht, die Dinge wieder zum Laufen zu bringen.
Harpastum
Grundelemente haben ihren eigenen Typ, "@" bezeichnet Objekte und danach erscheint der Klassenname zwischen Anführungszeichen. Die einzige Ausnahme ist id, die einfach als "T @"
Mihai Timar
2

Für Swift-Zuschauer können Sie diese Funktionalität erhalten, indem Sie die EncodableFunktionalität nutzen. Ich werde erklären, wie:

  1. Passen Sie Ihr Objekt an das EncodableProtokoll an

    class ExampleObj: NSObject, Encodable {
        var prop1: String = ""
        var prop2: String = ""
    }
  2. Erstellen Sie eine Erweiterung für Encodable, um toDictionaryFunktionen bereitzustellen

     public func toDictionary() -> [String: AnyObject]? {
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
        guard let data =  try? encoder.encode(self),
              let json = try? JSONSerialization.jsonObject(with: data, options: .init(rawValue: 0)), let jsonDict = json as? [String: AnyObject] else {
            return nil
        }
        return jsonDict
    }
  3. Rufen Sie toDictionaryIhre Objektinstanz auf und greifen Sie auf die keysEigenschaft zu.

    let exampleObj = ExampleObj()
    exampleObj.toDictionary()?.keys
  4. Voila! Greifen Sie wie folgt auf Ihre Immobilien zu:

    for k in exampleObj!.keys {
        print(k)
    }
    // Prints "prop1"
    // Prints "prop2"
Harry Bloom
quelle
1

Diese Antworten sind hilfreich, aber ich brauche mehr davon. Ich möchte nur überprüfen, ob der Klassentyp einer Eigenschaft dem eines vorhandenen Objekts entspricht. Alle oben genannten Codes sind dazu nicht in der Lage, weil: Um den Klassennamen eines Objekts abzurufen, gibt object_getClassName () folgende Texte zurück:

__NSArrayI (for an NSArray instance)
__NSArrayM (for an NSMutableArray instance)
__NSCFBoolean (an NSNumber object initialized by initWithBool:)
__NSCFNumber (an NSValue object initialized by [NSNumber initWithBool:])

Wenn Sie jedoch getPropertyType (...) aus dem obigen Beispielcode aufrufen, verwenden Sie 4 objc_property_t-Strukturen von Eigenschaften einer Klasse, die wie folgt definiert sind:

@property (nonatomic, strong) NSArray* a0;
@property (nonatomic, strong) NSArray* a1;
@property (nonatomic, copy) NSNumber* n0;
@property (nonatomic, copy) NSValue* n1;

Es werden Zeichenfolgen wie folgt zurückgegeben:

NSArray
NSArray
NSNumber
NSValue

Es kann also nicht festgestellt werden, ob ein NSObject der Wert einer Eigenschaft der Klasse sein kann. Wie geht das dann?

Hier ist mein vollständiger Beispielcode (die Funktion getPropertyType (...) ist dieselbe wie oben):

#import <objc/runtime.h>

@interface FOO : NSObject

@property (nonatomic, strong) NSArray* a0;
@property (nonatomic, strong) NSArray* a1;
@property (nonatomic, copy) NSNumber* n0;
@property (nonatomic, copy) NSValue* n1;

@end

@implementation FOO

@synthesize a0;
@synthesize a1;
@synthesize n0;
@synthesize n1;

@end

static const char *getPropertyType(objc_property_t property) {
    const char *attributes = property_getAttributes(property);
    //printf("attributes=%s\n", attributes);
    char buffer[1 + strlen(attributes)];
    strcpy(buffer, attributes);
    char *state = buffer, *attribute;
    while ((attribute = strsep(&state, ",")) != NULL) {
        if (attribute[0] == 'T' && attribute[1] != '@') {
            // it's a C primitive type:

            // if you want a list of what will be returned for these primitives, search online for
            // "objective-c" "Property Attribute Description Examples"
            // apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc.

            NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];
            return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
        }
        else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {
            // it's an ObjC id type:
            return "id";
        }
        else if (attribute[0] == 'T' && attribute[1] == '@') {
            // it's another ObjC object type:
            NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding];
            return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
        }
    }
    return "";
}

int main(int argc, char * argv[]) {
    NSArray* a0 = [[NSArray alloc] init];
    NSMutableArray* a1 = [[NSMutableArray alloc] init];
    NSNumber* n0 = [[NSNumber alloc] initWithBool:YES];
    NSValue* n1 = [[NSNumber alloc] initWithBool:NO];
    const char* type0 = object_getClassName(a0);
    const char* type1 = object_getClassName(a1);
    const char* type2 = object_getClassName(n0);
    const char* type3 = object_getClassName(n1);

    objc_property_t property0 = class_getProperty(FOO.class, "a0");
    objc_property_t property1 = class_getProperty(FOO.class, "a1");
    objc_property_t property2 = class_getProperty(FOO.class, "n0");
    objc_property_t property3 = class_getProperty(FOO.class, "n1");
    const char * memberthype0 = getPropertyType(property0);//property_getAttributes(property0);
    const char * memberthype1 = getPropertyType(property1);//property_getAttributes(property1);
    const char * memberthype2 = getPropertyType(property2);//property_getAttributes(property0);
    const char * memberthype3 = getPropertyType(property3);//property_getAttributes(property1);
    NSLog(@"%s", type0);
    NSLog(@"%s", type1);
    NSLog(@"%s", type2);
    NSLog(@"%s", type3);
    NSLog(@"%s", memberthype0);
    NSLog(@"%s", memberthype1);
    NSLog(@"%s", memberthype2);
    NSLog(@"%s", memberthype3);

    return 0;
}
godspeed1024
quelle