2012-08-02 37 views
30

Có cách nào để có được một mảng các thuộc tính lớp của một loại nào đó không? Ví dụ: nếu tôi có giao diện như thế nàyDanh sách các thuộc tính lớp trong Objective-C

@interface MyClass : NSObject 
    @property (strong,nonatomic) UILabel *firstLabel; 
    @property (strong,nonatomic) UILabel *secondLabel;   
@end 

tôi có thể tham chiếu đến các nhãn đó khi triển khai mà không biết tên của chúng không?

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

Tôi biết tôi có thể làm điều đó một cách dễ dàng với [NSArray arrayWithObjects:firstLabel,secondLabel,nil], nhưng tôi muốn làm điều đó với một số loại lớp liệt kê như for (UILabel* oneLabel in ???[self objects]???)

+7

Đừng đặt tên cho phương pháp với các 'get 'tiền tố; chỉ giới hạn trong một trường hợp sử dụng rất cụ thể và đây không phải là trường hợp đó. – bbum

+4

Trên iOS, hãy cân nhắc sử dụng IBOutletCollection. Điều này là khá nhiều những gì nó tồn tại. http://www.bobmccune.com/2011/01/31/using-ios-4s-iboutletcollection/ – isaac

Trả lời

76

Vì vậy, chính xác hơn, bạn muốn năng động, thời gian chạy observaion các thuộc tính, nếu tôi đã nhận nó một cách chính xác. Làm một cái gì đó như thế này (thực hiện phương pháp này trên tự, lớp bạn muốn nội quan):

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

Sử dụng nó như thế này:

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

Hope this helps.

+0

hoạt động này thực sự tốt đẹp! Chỉ, làm thế nào tôi có thể truy cập các thuộc tính sau này? Điều này mang lại cho tôi chuỗi tên, nhưng không phải con trỏ thực tế cho các đối tượng ... nó phải được thực sự dễ dàng tôi đoán :) im chỉ không quen thuộc với objc/thời gian chạy lớp :) Cảm ơn! –

+0

để tiết kiệm hơn, ví dụ như - id oneProperty = property ... –

+0

@animal_chin Tôi đã cập nhật câu trả lời của mình. –

7

Check-out link này. Nó là một wrapper c mục tiêu trên thời gian chạy C mục tiêu.

Bạn có thể sử dụng mã như dưới đây

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

Đừng quên #import Jonathan

6

Bạn phải bao gồm các tiêu đề runtime

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

Câu trả lời đã chỉnh sửa, cảm ơn chỉnh sửa – Vlad

11

sử dụng attributeKeys phương pháp NSObject.

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

     id attribute = [self valueForKey:key]; 

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

điều này trông giống như giải pháp thanh lịch nhất nhưng đáng buồn là phương pháp attributeKeys chỉ khả dụng trong Mac OS X chứ không phải trong iOS ... –

-1

Giải pháp của serhats là rất tiếc là nó không hoạt động cho iOS (như bạn đã đề cập) (và câu hỏi này được gắn thẻ cho iOS). Một giải pháp khác là để có được một đại diện NSDictionary của đối tượng và sau đó truy cập nó bình thường như các cặp khóa-giá trị. Tôi muốn giới thiệu một thể loại cho NSObject:

header-File:

@interface NSObject (NSDictionaryRepresentation) 

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

@end 

thi-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 

vay từ bài viết này: http://hesh.am/2013/01/transform-properties-of-an-nsobject-into-an-nsdictionary/

Bằng cách này bạn có thể làm điều gì đó tương tự như serhats được đề cập:

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

Câu trả lời của @ user529758 sẽ không hoạt động với ARC và nó sẽ không liệt kê các thuộc tính của bất kỳ lớp tổ tiên nào.

Để khắc phục điều này, bạn cần phải đi qua phân cấp lớp và sử dụng ARC2 tương thích [NSObject valueForKey:] để nhận giá trị thuộc tính.

Person.h:

#import <Foundation/Foundation.h> 

extern NSMutableArray *propertyNamesOfClass(Class klass); 

@interface Person : NSObject 

@property (nonatomic) NSString *name; 

@end 

Person.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; 
} 
Các vấn đề liên quan