List of class properties in Objective-C

前端 未结 6 1921
南方客
南方客 2020-11-29 17:32

Is there a way to get an array of class properties of certain kind? For example if i have interface like this

@interface MyClass : NSObject
    @property (st         


        
相关标签:
6条回答
  • 2020-11-29 17:51

    The solution of serhats is great unfortunately it doesn't work for iOS (as you mentioned) (and this question is tagged for iOS). A workaround would be to get a NSDictionary representation of the object and then access it normally as key-value pairs. I would recommend a category for NSObject:

    Header-File:

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

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

    Borrowed from this article: http://hesh.am/2013/01/transform-properties-of-an-nsobject-into-an-nsdictionary/

    This way you could do something similar as serhats mentioned:

    for (NSString *key in objectDic.allKeys) {
       if([objectDic[key] isKindOfClass:[UILabel  class]])
       {
           //put attribute to your array
       }
    }
    
    0 讨论(0)
  • 2020-11-29 17:55

    So more precisely, you want dynamic, runtime observaion of the properties, if I got it correctly. Do something like this (implement this method on self, the class you want to introspect):

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

    Use it like this:

    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 讨论(0)
  • 2020-11-29 17:55

    You must include the runtime headers

     #import<objc/runtime.h>
    uint propertiesCount;
    objc_property_t *classPropertiesArray = class_copyPropertyList([self class], &propertiesCount);
    free(classPropertiesArray);
    
    0 讨论(0)
  • 2020-11-29 17:55

    The answer by @user529758 won't work with ARC and it won't list the properties of any ancestor classes.

    To fix this, you need to traverse up the class hierarchy, and use the ARC-compatible [NSObject valueForKey:] to get the property values.

    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;
    }
    
    0 讨论(0)
  • 2020-11-29 18:02

    Check out this link. It is an objective c wrapper over objective C runtime.

    You can use code like below

    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 讨论(0)
  • 2020-11-29 18:10

    use attributeKeys method of NSObject.

        for (NSString *key in [self attributeKeys]) {
    
            id attribute = [self valueForKey:key];
    
            if([attribute isKindOfClass:[UILabel  class]])
            {
             //put attribute to your array
            }
        }
    
    0 讨论(0)
提交回复
热议问题