2012-05-21 14 views
6

Próbuję uzyskać listę wszystkich właściwości nieznanej klasy i klasy każdej własności. W tym momencie otrzymuję listę wszystkich właściwości obiektu (robię to rekurencyjnie, aby uzyskać wszystkie superklasy). I inspirowane w this postiOS get property class

+ (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]; 
} 

Więc teraz chcę klasę każdej nieruchomości i robię:

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]); 
} 

Problemem jest to, że pracuje dla NSStrings lub ale nie dla klas niestandardowych, na które zwraca mnie zero. Chcę rekurencyjnie utworzyć słownik, który reprezentuje obiekt, który może mieć inne obiekty wewnątrz i jak myślę, że muszę znać klasę każdej własności, czy to możliwe?

Odpowiedz

2

Powinieneś prawdopodobnie przechowywać klasę (jako ciąg) dla każdej właściwości w tym samym czasie, w którym przechowuje się propertyName. Może jako słownik o nazwie właściwości jako klucz i nazwa klasy jako wartość lub na odwrót.

Aby uzyskać nazwę klasy, można zrobić coś takiego (put to prawo po zadeklarować propertyName):

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

Ciąg kodu obsługi dlatego atrybuty obejmują liczbę sztuk informacje - Dokładne szczegóły są podane tutaj: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html

+0

Człowiek, który jest niesamowity i działa idealnie! Dzięki za pomoc :) – Jpellat

+0

Nie ma za co :) –

2

AKTUALIZACJA

ta nie działa dla wartości, które są nil. Zamiast tego należy użyć API środowiska wykonawczego C, aby uzyskać klasę z odpowiedniej metody ivar lub accessor.

+0

Jeśli właściwość jest 'nil', w jaki sposób to przyniesie klasy? – jlehr

+0

Dzięki! Zobacz wartości zerowe :) – Jpellat

+0

Nie ma za co. Inną kwestią jest to, że wywoływanie metod akcesorów może powodować efekty uboczne - na przykład leniwą inicjalizację - które mogą nie być wyzwalane w tym momencie. – jlehr

7

Po prostu zrobiłem na to małą metodę.

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

Można zoptymalizować na wiele sposobów, ale uwielbiam to.


Realizacja idzie tak:

-(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; 
} 

Jest częścią eppz!kit ciągu rozwijającym obiekt representer nazywa NSObject+EPPZRepresentable.h. W rzeczywistości robi to, co masz pierwotnie osiągnąć.

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

koduje on wiele typów, iteracyjne zbiory koryta, reprezentuje rodzaje CoreGraphics, UIColors, również reprezentować/zrekonstruować odwołań do obiektów.


Nowa wersja wypluwa z powrotem nawet nazwy typu C i wymienione typy struct, a także:

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

Część eppz!model, zachęcamy do korzystania z wdrożenia metody na https://github.com/eppz/eppz.model/blob/master/eppz!model/NSObject%2BEPPZModel_inspecting.m#L111

+3

Działa jak urok! – Kjuly

0

Następujące elementy dodane do kategorii NSObject to dobra sztuczka.

- (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; 
}