2012-05-21 17 views
6

Sto cercando di ottenere un elenco di tutte le proprietà di una classe sconosciuta e della classe di ogni proprietà. Nel momento in cui ottengo un elenco di tutte le proprietà di un oggetto (lo faccio ricorsivamente per ottenere tutte le superclassi). Mi ispiro a this postiOS ottenere la classe di proprietà

+ (NSArray *)classPropsFor:(Class)klass 
{  
    NSLog(@"Properties for class:%@", klass); 
    if (klass == NULL || klass == [NSObject class]) { 
     return nil; 
    } 

    NSMutableArray *results = [[NSMutableArray 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) { 
      NSString *propertyName = [NSString stringWithUTF8String:propName]; 
      [results addObject:propertyName]; 
     } 
     NSArray* dict = [self classPropsFor:[klass superclass]]; 
     [results addObjectsFromArray:dict]; 
    } 
    free(properties); 

    return [NSArray arrayWithArray:results]; 
} 

Così ora voglio la classe di ogni proprietà e lo faccio:

NSArray* properties = [PropertyUtil classPropsFor:[self class]]; 
for (NSString* property in properties) { 
    id value= [self valueForKey:property]; 
    NSLog(@"Value class for key: %@ is %@", property, [value class]); 
} 

Il problema è che funziona per NSStrings o, ma non per classi personalizzate, per quel che mi ritorna nullo. Voglio creare ricorsivamente un dizionario che rappresenti un oggetto che possa avere altri oggetti all'interno e come penso debba conoscere la classe di ogni proprietà, è possibile?

risposta

2

Probabilmente dovresti memorizzare la classe (come stringa) per ogni proprietà nello stesso momento in cui memorizzi propertyName. Forse come dizionario con il nome della proprietà come chiave e nome della classe come valore, o viceversa.

Per ottenere il nome della classe, si può fare qualcosa di simile (mettere questo subito dopo si dichiara propertyName):

NSString* propertyAttributes = [NSString stringWithUTF8String:property_getAttributes(property)]; 
NSArray* splitPropertyAttributes = [propertyAttributes componentsSeparatedByString:@"\""]; 
if ([splitPropertyAttributes count] >= 2) 
{ 
    NSLog(@"Class of property: %@", [splitPropertyAttributes objectAtIndex:1]); 
} 

il codice che gestisce stringa è perché gli attributi comprendono un certo numero di pezzi di informazioni - il dettagli esatti sono specificati qui: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html

+0

L'uomo è fantastico e funziona perfettamente! Grazie per il tuo aiuto :) – Jpellat

+0

Prego :) –

2

AGGIORNATO

questo non funziona per i valori che sono nil. Invece si dovrebbe usare l'API C runtime per ottenere la classe dal corrispondente metodo ivar o accessor.

+0

Se la proprietà è 'nil', come arriverà questa classe? – jlehr

+0

Grazie! Beh, guarda per i valori nil :) – Jpellat

+0

Prego. Un'altra considerazione è che invocare i metodi di accesso potrebbe avere effetti collaterali, ad esempio l'inizializzazione pigra, che potresti non voler attivare a questo punto. – jlehr

7

Ho appena fatto un piccolo metodo per questo.

// Simple as. 
Class propertyClass = [customObject classOfPropertyNamed:propertyName]; 

Potrebbe essere ottimizzato in molti modi, ma lo adoro.


Attuazione va come:

-(Class)classOfPropertyNamed:(NSString*) propertyName 
{ 
    // Get Class of property to be populated. 
    Class propertyClass = nil; 
    objc_property_t property = class_getProperty([self class], [propertyName UTF8String]); 
    NSString *propertyAttributes = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding]; 
    NSArray *splitPropertyAttributes = [propertyAttributes componentsSeparatedByString:@","]; 
    if (splitPropertyAttributes.count > 0) 
    { 
     // xcdoc://ios//library/prerelease/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html 
     NSString *encodeType = splitPropertyAttributes[0]; 
     NSArray *splitEncodeType = [encodeType componentsSeparatedByString:@"\""]; 
     NSString *className = splitEncodeType[1]; 
     propertyClass = NSClassFromString(className); 
    } 
    return propertyClass; 
} 

Fa parte del eppz!kit, all'interno di un oggetto representer sviluppo chiamato NSObject+EPPZRepresentable.h. In realtà fa ciò che devi raggiungere in origine.

// Works vica-versa. 
NSDictionary *representation = [customObject dictionaryRepresentation]; 
CustomClass = [CustomClass representableWithDictionaryRepresentation:representation]; 

Codifica molti tipi, collezioni trogolo iterazione, rappresenta CoreGraphics tipi, UIColors, rappresentano anche/ricostruire i riferimenti agli oggetti.


nuova versione si sputa indietro anche nomi di tipo C e tipi struct nome così:

NSLog(@"%@", [self typeOfPropertyNamed:@"index"]); // unsigned int 
NSLog(@"%@", [self typeOfPropertyNamed:@"area"]); // CGRect 
NSLog(@"%@", [self typeOfPropertyNamed:@"keyColor"]); // UIColor 

parte di eppz!model, sentitevi liberi di utilizzare il metodo implementazioni a https://github.com/eppz/eppz.model/blob/master/eppz!model/NSObject%2BEPPZModel_inspecting.m#L111

+3

Funziona come un fascino! – Kjuly

0

Il seguente aggiunto a una categoria NSObject fa il trucco.

- (Class) classForKeyPath:(NSString*)keyPath { 
    Class class = 0; 

    unsigned int n = 0; 
    objc_property_t* properties = class_copyPropertyList(self.class, &n); 
    for (unsigned int i=0; i<n; i++) { 
     objc_property_t* property = properties + i; 
     NSString* name = [NSString stringWithCString:property_getName(*property) encoding:NSUTF8StringEncoding]; 
     if (![keyPath isEqualToString:name]) continue; 

     const char* attributes = property_getAttributes(*property); 
     if (attributes[1] == '@') { 
      NSMutableString* className = [NSMutableString new]; 
      for (int j=3; attributes[j] && attributes[j]!='"'; j++) 
       [className appendFormat:@"%c", attributes[j]]; 
      class = NSClassFromString(className); 
     } 
     break; 
    } 
    free(properties); 

    return class; 
} 
Problemi correlati