9

I want to create an Objective-C base class that performs an operation on all properties (of varying types) at runtime. Since the names and types of the properties will not always be known, how can I do something like this?

@implementation SomeBaseClass

- (NSString *)checkAllProperties
{
    for (property in properties) {
        // Perform a check on the property
    }
}

EDIT: This would be particularly useful in a custom - (NSString *)description: override.

Old McStopher
  • 6,295
  • 10
  • 60
  • 89

3 Answers3

22

To expand on mvds' answer (started writing this before I saw his), here's a little sample program that uses the Objective-C runtime API to loop through and print information about each property in a class:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface TestClass : NSObject

@property (nonatomic, retain) NSString *firstName;
@property (nonatomic, retain) NSString *lastName;
@property (nonatomic) NSInteger *age;

@end

@implementation TestClass

@synthesize firstName;
@synthesize lastName;
@synthesize age;

@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        unsigned int numberOfProperties = 0;
        objc_property_t *propertyArray = class_copyPropertyList([TestClass class], &numberOfProperties);

        for (NSUInteger i = 0; i < numberOfProperties; i++)
        {
            objc_property_t property = propertyArray[i];
            NSString *name = [[NSString alloc] initWithUTF8String:property_getName(property)];
            NSString *attributesString = [[NSString alloc] initWithUTF8String:property_getAttributes(property)];
            NSLog(@"Property %@ attributes: %@", name, attributesString);
        }
        free(propertyArray);
    }
}

Output:

Property age attributes: T^q,Vage
Property lastName attributes: T@"NSString",&,N,VlastName
Property firstName attributes: T@"NSString",&,N,VfirstName

Note that this program needs to be compiled with ARC turned on.

Andrew Madsen
  • 21,309
  • 5
  • 56
  • 97
  • A good followup question would be how to return the values of each dynamically... I'm thinking dynamically create an accessor, but syntactically, I'm still trying to figure that out. – Old McStopher Feb 14 '12 at 00:07
  • 1
    There are a couple ways you could do that, but the most straightforward would be to use KVC: `id value = [self valueForKey:@"propertyName"]`. Gets a little more complicated where you have both primitive (int, float, etc) and object (NSString, etc) return types, but the basic premise will work. – Andrew Madsen Feb 14 '12 at 00:37
  • It's meant to be compiled with ARC turned on (should have mentioned that in my answer). – Andrew Madsen Feb 14 '12 at 03:54
  • [self valueForKey:@"propertyName"] works for basic types, what about self defined types? how to get the properties for custom class? thanks!! – flypig Sep 07 '12 at 16:02
  • 1
    I may not understand exactly what you're asking. `[self valueForKey:@"propertyName"]` works just fine for properties with a type which is a custom class. `-valueForKey:`'s return type is id. – Andrew Madsen Sep 07 '12 at 16:25
15

Use

objc_property_t * class_copyPropertyList(Class cls, unsigned int *outCount)

and read https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html on how to do this exactly.

Some code to get you going:

#import <objc/runtime.h>

unsigned int count=0;
objc_property_t *props = class_copyPropertyList([self class],&count);
for ( int i=0;i<count;i++ )
{
    const char *name = property_getName(props[i]); 
    NSLog(@"property %d: %s",i,name);
}
mvds
  • 45,755
  • 8
  • 102
  • 111
  • 1
    Stellar. Perfect resource for closer-to-the-metal runtime madness. – Old McStopher Feb 13 '12 at 23:18
  • Thanks a lot, works good, how do i copy one objects properties to other at runtime? – Pavan Dec 27 '12 at 14:43
  • 1
    @PavanSaberjack using `valueForKey:` and `setValue:forKey:`, where you have to take notice that `char *name` cannot be used as object (i.e. as `NSString`) without some form of conversion. – mvds Dec 28 '12 at 01:16
  • @mvds I'm having this problem where `class_copyPropertyList` not returning the parent class property list when we use that code in a subclass, any idea how to solve that? – Laky Apr 01 '16 at 10:28
0

Adding some detial to @mvds:

unsigned int count=0;
objc_property_t *props = class_copyPropertyList([self class],&count);
for ( int i=0;i<count;i++ )
{
    const char *name = property_getName(props[i]);
    NSString* dataToGet = [NSString swf:@"%s",name];
    @try { // in case of this pair not key value coding-compliant
        id value = [barButton valueForKey:dataToGet];
        NSLog(@"prop %d: %s  %@",i,name, value);
    } @catch (NSException *exception) {
        // NSLog(@"Exception:%@",exception);
    }
    @finally {
        // Display Alternative
    }
}

Please give @mvds the upvote.

Jeff
  • 2,659
  • 1
  • 22
  • 41