2012-08-02 12 views
30

Czy istnieje sposób na uzyskanie tablicy właściwości klasy określonego rodzaju? Na przykład, czy mam taki interfejs, aby uzyskać odniesienie do tych etykiet we wdrożeniu bez znajomości ich nazwy?Lista właściwości klas w Objective-C

@implementation MyClass 
    -(NSArray*)getListOfAllLabels 
    { 
      ????? 
    }   
@end 

Wiem, że może to zrobić z łatwością [NSArray arrayWithObjects:firstLabel,secondLabel,nil], ale chciałbym zrobić to z jakiejś klasy wyliczenie jak for (UILabel* oneLabel in ???[self objects]???)

+7

Nie nazwać Metody z 'dostać 'prefiks; to jest ograniczone tylko do bardzo specyficznego przypadku użycia i to nie jest to. – bbum

+4

W systemie iOS należy rozważyć użycie IBOutletCollection. Jest to prawie wszystko, za czym istnieje. http://www.bobmccune.com/2011/01/31/using-ios-4s-iboutletcollection/ – isaac

Odpowiedz

76

Tak dokładniej, chcesz dynamiczny, wykonawcze observaion właściwości, jeśli mam go poprawnie. Zrobić coś takiego (wdrożenia tej metody na siebie, klasa chcesz introspekcji):

#import <objc/runtime.h> 

- (NSArray *)allPropertyNames 
{ 
    unsigned count; 
    objc_property_t *properties = class_copyPropertyList([self class], &count); 

    NSMutableArray *rv = [NSMutableArray array]; 

    unsigned i; 
    for (i = 0; i < count; i++) 
    { 
     objc_property_t property = properties[i]; 
     NSString *name = [NSString stringWithUTF8String:property_getName(property)]; 
     [rv addObject:name]; 
    } 

    free(properties); 

    return rv; 
} 

- (void *)pointerOfIvarForPropertyNamed:(NSString *)name 
{ 
    objc_property_t property = class_getProperty([self class], [name UTF8String]); 

    const char *attr = property_getAttributes(property); 
    const char *ivarName = strchr(attr, 'V') + 1; 

    Ivar ivar = object_getInstanceVariable(self, ivarName, NULL); 

    return (char *)self + ivar_getOffset(ivar); 
} 

Używaj go tak:

SomeType myProperty; 
NSArray *properties = [self allPropertyNames]; 
NSString *firstPropertyName = [properties objectAtIndex:0]; 
void *propertyIvarAddress = [self getPointerOfIvarForPropertyNamed:firstPropertyName]; 
myProperty = *(SomeType *)propertyIvarAddress; 

// Simpler alternative using KVC: 
myProperty = [self valueForKey:firstPropertyName]; 

nadzieję, że to pomaga.

+0

działa naprawdę fajnie! Tylko jak mogę uzyskać dostęp do tych właściwości po tym? To daje mi ciągi nazw, ale nie rzeczywiste wskazówki do obiektów ... to musi być naprawdę łatwe, chyba :) nie jestem zaznajomiony z klasą objc/runtime :) Dzięki! –

+0

by być bardziej conrete, coś jak - id oneProperty = własność ... –

+0

@animal_chin Zaktualizowałem moją odpowiedź. –

7

Sprawdź tę link. Jest to obiektywne otoczenie c względem obiektywnego środowiska wykonawczego C.

Można użyć kodu jak poniżej

uint count; 
objc_property_t* properties = class_copyPropertyList(self.class, &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); 
+0

Nie zapomnij o #import Jonathan

6

musi zawierać nagłówki uruchomieniowe

#import<objc/runtime.h> 
uint propertiesCount; 
objc_property_t *classPropertiesArray = class_copyPropertyList([self class], &propertiesCount); 
free(classPropertiesArray); 
+0

Edytowana odpowiedź, dzięki za poprawkę – Vlad

11

użytku attributeKeys metoda NSObject.

for (NSString *key in [self attributeKeys]) { 

     id attribute = [self valueForKey:key]; 

     if([attribute isKindOfClass:[UILabel class]]) 
     { 
     //put attribute to your array 
     } 
    } 
+12

to wygląda na najbardziej eleganckie rozwiązanie, ale niestety metoda attributeKeys jest dostępna tylko w systemie Mac OS X, a nie w systemie iOS ... –

-1

Rozwiązanie serhats jest wielki niestety nie działa na iOS (jak wspomniano) (i to pytanie jest oznaczone na iOS). Obejściem byłoby uzyskanie NSDictionary reprezentacji obiektu, a następnie dostęp do niego normalnie jako pary klucz-wartość. Polecam kategorię na NSObject:

nagłówka pliku:

@interface NSObject (NSDictionaryRepresentation) 

/** 
Returns an NSDictionary containing the properties of an object that are not nil. 
*/ 
- (NSDictionary *)dictionaryRepresentation; 

@end 

Wdrożenie-File:

#import "NSObject+NSDictionaryRepresentation.h" 
#import <objc/runtime.h> 

@implementation NSObject (NSDictionaryRepresentation) 

- (NSDictionary *)dictionaryRepresentation { 
    unsigned int count = 0; 
    // Get a list of all properties in the class. 
    objc_property_t *properties = class_copyPropertyList([self class], &count); 

    NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithCapacity:count]; 

    for (int i = 0; i < count; i++) { 
     NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])]; 
     NSString *value = [self valueForKey:key]; 

     // Only add to the NSDictionary if it's not nil. 
     if (value) 
      [dictionary setObject:value forKey:key]; 
    } 

    free(properties); 

    return dictionary; 
} 

@end 

zapożyczonych z tego artykułu: http://hesh.am/2013/01/transform-properties-of-an-nsobject-into-an-nsdictionary/

ten sposób mógł zrób coś podobnego do wspomnianych serów:

for (NSString *key in objectDic.allKeys) { 
    if([objectDic[key] isKindOfClass:[UILabel class]]) 
    { 
     //put attribute to your array 
    } 
} 
0

Odpowiedź @ user529758 nie zadziała z ARC i nie będzie zawierała właściwości żadnych klas przodka.

Aby to naprawić, musisz przejść do hierarchii klas i użyć zgodnego z ARC [NSObject valueForKey:], aby uzyskać wartości właściwości.

Person.h:

#import <Foundation/Foundation.h> 

extern NSMutableArray *propertyNamesOfClass(Class klass); 

@interface Person : NSObject 

@property (nonatomic) NSString *name; 

@end 

osoby.m:

#import "Person.h" 
#import <objc/runtime.h> 

NSMutableArray *propertyNamesOfClass(Class klass) { 
    unsigned int count; 
    objc_property_t *properties = class_copyPropertyList(klass, &count); 

    NSMutableArray *rv = [NSMutableArray array]; 

    for (unsigned int i = 0; i < count; i++) 
    { 
     objc_property_t property = properties[i]; 
     NSString *name = [NSString stringWithUTF8String:property_getName(property)]; 
     [rv addObject:name]; 
    } 

    free(properties); 

    return rv; 
} 

@implementation Person 

- (NSMutableArray *)allPropertyNames { 
    NSMutableArray *classes = [NSMutableArray array]; 
    Class currentClass = [self class]; 
    while (currentClass != nil && currentClass != [NSObject class]) { 
     [classes addObject:currentClass]; 
     currentClass = class_getSuperclass(currentClass); 
    } 

    NSMutableArray *names = [NSMutableArray array]; 
    [classes enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(Class currentClass, NSUInteger idx, BOOL *stop) { 
     [names addObjectsFromArray:propertyNamesOfClass(currentClass)]; 
    }]; 

    return names; 
} 

- (NSString*)description { 
    NSMutableArray *keys = [self allPropertyNames]; 
    NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithCapacity:keys.count]; 
    [keys enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) { 
     properties[key] = [self valueForKey:key]; 
    }]; 

    NSString *className = NSStringFromClass([self class]); 
    return [NSString stringWithFormat:@"%@ : %@", className, properties]; 
} 

Student.h:

#import "Person.h" 

@interface Student : Person 

@property (nonatomic) NSString *studentID; 

@end 

Student.m:

#import "Student.h" 

@implementation Student 

@end 

main.m:

#import <Foundation/Foundation.h> 
#import "Student.h" 

int main(int argc, const char * argv[]) { 
    @autoreleasepool { 
     // insert code here... 
     Student *student = [[Student alloc] init]; 
     student.name = @"John Doe"; 
     student.studentID = @"123456789"; 
     NSLog(@"student - %@", student); 
    } 
    return 0; 
} 
Powiązane problemy