2012-05-21 35 views
6

Tôi đang cố gắng lấy danh sách tất cả các thuộc tính của một lớp không xác định và lớp của mọi thuộc tính. Bởi thời điểm này tôi nhận được một danh sách của tất cả các thuộc tính của một đối tượng (tôi làm nó đệ quy để có được tất cả các siêu lớp). Tôi lấy cảm hứng từ trong this postiOS nhận được loại tài sản

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

Vì vậy, bây giờ tôi muốn lớp của tất cả các tài sản và tôi làm:

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

Vấn đề là nó hoạt động cho NSStrings hay nhưng không phải cho các lớp tùy chỉnh, cho rằng nó sẽ trả về cho tôi vô giá trị. Tôi muốn đệ quy tạo một từ điển đại diện cho một đối tượng có thể có các đối tượng khác bên trong và khi tôi nghĩ rằng tôi cần phải biết lớp của mọi thuộc tính, có khả thi không?

Trả lời

2

Bạn có thể lưu trữ lớp (dưới dạng chuỗi) cho mỗi thuộc tính cùng một lúc khi bạn lưu trữ propertyName. Có thể là từ điển có tên thuộc tính làm khóa và tên lớp làm giá trị hoặc ngược lại.

Để có được tên lớp, bạn có thể làm một cái gì đó như thế này (đặt quyền này sau khi bạn khai báo propertyName):

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

Chuỗi xử lý mã là vì các thuộc tính bao gồm một số mẩu thông tin - chi tiết chính xác được chỉ định tại đây: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html

+0

Con người tuyệt vời và hoạt động hoàn hảo! Cảm ơn sự giúp đỡ của bạn :) – Jpellat

+0

Bạn được chào đón :) –

2

CẬP NHẬT

này không làm việc cho các giá trị mà nil. Thay vào đó, bạn nên sử dụng API C thời gian chạy để lấy lớp từ phương thức truy cập hoặc ivar tương ứng.

+0

Nếu thuộc tính là 'nil', điều này sẽ mang lại lợi ích cho lớp học như thế nào? – jlehr

+0

Cảm ơn! Xem các giá trị nil :) – Jpellat

+0

Bạn được chào đón. Một xem xét khác là việc gọi các phương thức accessor có thể có các tác dụng phụ - ví dụ, khởi tạo lười biếng - mà bạn có thể không muốn kích hoạt tại thời điểm này. – jlehr

7

Chỉ cần thực hiện một phương pháp nhỏ cho việc này.

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

Có thể được tối ưu hóa theo nhiều cách nhưng tôi thích nó.


Thực hiện đi như thế:

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

Nó là một phần của eppz!kit, trong một đối tượng đang phát triển representer gọi NSObject+EPPZRepresentable.h. Nó thực sự làm những gì bạn đang đạt được ban đầu.

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

Nó mã hóa nhiều loại, lặp lại các bộ sưu tập máng, đại diện cho các loại CoreGraphics, UIColors, cũng thể hiện/tái tạo lại các tham chiếu đối tượng.


Phiên bản mới spits bạn trở lại thậm chí tên loại Cloại struct tên cũng như:

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

Một phần của eppz!model, cảm thấy tự do để sử dụng triển khai phương pháp tại https://github.com/eppz/eppz.model/blob/master/eppz!model/NSObject%2BEPPZModel_inspecting.m#L111

+3

Nó hoạt động như một sự quyến rũ! – Kjuly

0

Sau đây được thêm vào danh mục NSObject thực hiện thủ thuật.

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