Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to dynamically determine ivars of a class at runtime in Cocoa / Cocoa Touch?

Is there a way to obtain something like a dictionary of all key-value pairs of a class?

like image 895
Corey Floyd Avatar asked Oct 27 '09 14:10

Corey Floyd


3 Answers

You'd have to roll your own using the Objective-C Runtime functions. Here's some very basic sample code. Note that getting the ivars of a class doesn't get the ivars of its superclass. You'd need to do that explicitly, but the functions are all there in the runtime.

#import <objc/objc-runtime.h>
#include <inttypes.h>
#include <Foundation/Foundation.h>

@interface Foo : NSObject
{
    int i1;
}
@end
@implementation Foo
@end

@interface Bar : Foo
{
    NSString* s1;
}

@end
@implementation Bar
@end

int main(int argc, char** argv)
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    unsigned int count;
    Ivar* ivars = class_copyIvarList([Bar class], &count);
    for(unsigned int i = 0; i < count; ++i)
    {
        NSLog(@"%@::%s", [Bar class], ivar_getName(ivars[i]));
    }
    free(ivars);


    [pool release];
}
like image 52
nall Avatar answered Nov 03 '22 10:11

nall


I'm not sure for just ivars but if you have them defined as properties it is possible to access the available properties on a class.

I've been using SQLitePersistentObjects for a couple projects and it has some helpful code that gets the properties defined on the class to use when figuring out serialization to and from sqlite.

It uses the function class_copyPropertyList to get the available list of properties on a class.

More specifically:

+(NSDictionary *)propertiesWithEncodedTypes
{

    // DO NOT use a static variable to cache this, it will cause problem with subclasses of classes that are subclasses of SQLitePersistentObject

    // Recurse up the classes, but stop at NSObject. Each class only reports its own properties, not those inherited from its superclass
    NSMutableDictionary *theProps;

    if ([self superclass] != [NSObject class])
        theProps = (NSMutableDictionary *)[[self superclass] propertiesWithEncodedTypes];
    else
        theProps = [NSMutableDictionary dictionary];

    unsigned int outCount;


    objc_property_t *propList = class_copyPropertyList([self class], &outCount);
    int i;

    // Loop through properties and add declarations for the create
    for (i=0; i < outCount; i++)
    {
        objc_property_t * oneProp = propList + i;
        NSString *propName = [NSString stringWithUTF8String:property_getName(*oneProp)];
        NSString *attrs = [NSString stringWithUTF8String: property_getAttributes(*oneProp)];
        NSArray *attrParts = [attrs componentsSeparatedByString:@","];
        if (attrParts != nil)
        {
            if ([attrParts count] > 0)
            {
                NSString *propType = [[attrParts objectAtIndex:0] substringFromIndex:1];
                [theProps setObject:propType forKey:propName];
            }
        }
    }

  free(propList);

    return theProps;    
}

This returns a dictionary of the properties - you'll need to do some investigating of the results you get back but you should be able to get what you need if you're using properties.

like image 6
paulthenerd Avatar answered Nov 03 '22 09:11

paulthenerd


Yep, totally possible:

int numIvars = 0;
Ivar * ivars = class_copyIvarList([anInstanceOfAClass class], &numIvars);
NSMutableDictionary * pairs = [NSMutableDictionary dictionary];
for (int i = 0; i < numIvars; ++i) {
  Ivar ivar = ivars[i];
  NSString * ivarName = [NSString stringWithCString:ivar_getName(ivar) encoding:NSUTF8StringEncoding];
  id ivarValue = [anInstanceOfAClass valueForKey:ivarName];
  [pairs setObject:ivarValue forKey:ivarName];
}
free(ivars);
NSLog(@"%@", pairs);
like image 4
Dave DeLong Avatar answered Nov 03 '22 09:11

Dave DeLong