Objective-C Introspection/Reflection

43,416

Solution 1

UPDATE: Anyone looking to do this kind of stuff might want to check out Mike Ash's ObjC wrapper for the Objective-C runtime.

This is more or less how you'd go about it:

#import <objc/runtime.h>

. . . 

-(void)dumpInfo
{
    Class clazz = [self class];
    u_int count;

    Ivar* ivars = class_copyIvarList(clazz, &count);
    NSMutableArray* ivarArray = [NSMutableArray arrayWithCapacity:count];
    for (int i = 0; i < count ; i++)
    {
        const char* ivarName = ivar_getName(ivars[i]);
        [ivarArray addObject:[NSString  stringWithCString:ivarName encoding:NSUTF8StringEncoding]];
    }
    free(ivars);

    objc_property_t* properties = class_copyPropertyList(clazz, &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);

    Method* methods = class_copyMethodList(clazz, &count);
    NSMutableArray* methodArray = [NSMutableArray arrayWithCapacity:count];
    for (int i = 0; i < count ; i++)
    {
        SEL selector = method_getName(methods[i]);
        const char* methodName = sel_getName(selector);
        [methodArray addObject:[NSString  stringWithCString:methodName encoding:NSUTF8StringEncoding]];
    }
    free(methods);

    NSDictionary* classDump = [NSDictionary dictionaryWithObjectsAndKeys:
                               ivarArray, @"ivars",
                               propertyArray, @"properties",
                               methodArray, @"methods",
                               nil];

    NSLog(@"%@", classDump);
}

From there, it's easy to get the actual values of an instance's properties, but you have to check to see if they are primitive types or objects, so I was too lazy to put it in. You could also choose to scan the inheritance chain to get all the properties defined on an object. Then there are methods defined on categories, and more... But almost everything is readily available.

Here's an excerpt of what the above code dumps for UILabel:

{
    ivars =     (
        "_size",
        "_text",
        "_color",
        "_highlightedColor",
        "_shadowColor",
        "_font",
        "_shadowOffset",
        "_minFontSize",
        "_actualFontSize",
        "_numberOfLines",
        "_lastLineBaseline",
        "_lineSpacing",
        "_textLabelFlags"
    );
    methods =     (
        rawSize,
        "setRawSize:",
        "drawContentsInRect:",
        "textRectForBounds:",
        "textSizeForWidth:",
        . . .
    );
    properties =     (
        text,
        font,
        textColor,
        shadowColor,
        shadowOffset,
        textAlignment,
        lineBreakMode,
        highlightedTextColor,
        highlighted,
        enabled,
        numberOfLines,
        adjustsFontSizeToFitWidth,
        minimumFontSize,
        baselineAdjustment,
        "_lastLineBaseline",
        lineSpacing,
        userInteractionEnabled
    );
}

Solution 2

Short of the description method (like .toString() in Java), I haven't heard of one that was built in, but it wouldn't be too difficult to create one. The Objective-C Runtime Reference has a bunch of functions you can use to get information about an object's instance variables, methods, properties, etc.

Solution 3

Here's what I am currently using to automatically print class variables, in a library for eventual public release - it works by dumping all properties from the instance class all the way back up the inheritance tree. Thanks to KVC you don't need to care if a property is a primitive type or not (for most types).

// Finds all properties of an object, and prints each one out as part of a string describing the class.
+ (NSString *) autoDescribe:(id)instance classType:(Class)classType
{
    NSUInteger count;
    objc_property_t *propList = class_copyPropertyList(classType, &count);
    NSMutableString *propPrint = [NSMutableString string];

    for ( int i = 0; i < count; i++ )
    {
        objc_property_t property = propList[i];

        const char *propName = property_getName(property);
        NSString *propNameString =[NSString stringWithCString:propName encoding:NSASCIIStringEncoding];

        if(propName) 
        {
            id value = [instance valueForKey:propNameString];
            [propPrint appendString:[NSString stringWithFormat:@"%@=%@ ; ", propNameString, value]];
        }
    }
    free(propList);


    // Now see if we need to map any superclasses as well.
    Class superClass = class_getSuperclass( classType );
    if ( superClass != nil && ! [superClass isEqual:[NSObject class]] )
    {
        NSString *superString = [self autoDescribe:instance classType:superClass];
        [propPrint appendString:superString];
    }

    return propPrint;
}

+ (NSString *) autoDescribe:(id)instance
{
    NSString *headerString = [NSString stringWithFormat:@"%@:%p:: ",[instance class], instance];
    return [headerString stringByAppendingString:[self autoDescribe:instance classType:[instance class]]];
}

Solution 4

I made a couple of tweaks to Kendall's code for printing property values, which came in very handy for me. I defined it as an instance method instead of a class method, as that's how the superclass recursion calls it. I also added exception handling for non-KVO-compliant properties, and added line breaks to the output to make it easier to read (and diff):

-(NSString *) autoDescribe:(id)instance classType:(Class)classType
{
    NSUInteger count;
    objc_property_t *propList = class_copyPropertyList(classType, &count);
    NSMutableString *propPrint = [NSMutableString string];

    for ( int i = 0; i < count; i++ )
    {
        objc_property_t property = propList[i];

        const char *propName = property_getName(property);
        NSString *propNameString =[NSString stringWithCString:propName encoding:NSASCIIStringEncoding];

        if(propName) 
        {
         @try {
            id value = [instance valueForKey:propNameString];
            [propPrint appendString:[NSString stringWithFormat:@"%@=%@\n", propNameString, value]];
         }
         @catch (NSException *exception) {
            [propPrint appendString:[NSString stringWithFormat:@"Can't get value for property %@ through KVO\n", propNameString]];
         }
        }
    }
    free(propList);


    // Now see if we need to map any superclasses as well.
    Class superClass = class_getSuperclass( classType );
    if ( superClass != nil && ! [superClass isEqual:[NSObject class]] )
    {
        NSString *superString = [self autoDescribe:instance classType:superClass];
        [propPrint appendString:superString];
    }

    return propPrint;
}

Solution 5

Honestly, the right tool for this job is Xcode's debugger. It has all this information easily accessible in a visual way. Take the time to learn how to use it, it's a really powerful tool.

More information:

Using the Debugger

Outdated Xcode Debugging Guide - archived by Apple

About Debugging with Xcode - archived by Apple

About LLDB and Debugging - archived by Apple

Debugging with GDB - archived by Apple

SpriteKit Debugging Guide - archived by Apple

Debugging Programming Topics for Core Foundation - archived by Apple

Share:
43,416

Related videos on Youtube

Alan Storm
Author by

Alan Storm

Portland based Web Developer/Programmer/Engineer. Projects include No Frills Magento Layout, the only Magento layout book you'll ever need and Commerce Bug, the debugging extension for the Magento Ecommerce system. If you're interested in low cost, in-depth mentoring/tutoring, checkout my Patreon campaign.

Updated on July 05, 2022

Comments

  • Alan Storm
    Alan Storm almost 2 years

    Is there a built in method, function, API, commonly accepted way, etc. to dump the contents of an instantiated object in Objective-C, specifically in Apple's Cocoa/Cocoa-Touch environment?

    I want to be able to do something like

    MyType *the_thing = [[MyType alloc] init];
    NSString *the_dump = [the_thing dump]; //pseudo code
    NSLog("Dumped Contents: %@", the_dump);
    

    and have the object's instance variable names and values displayed, along with any methods available to call at run time. Ideally in an easy to read format.

    For developers familiar with PHP, I'm basically looking for the equivalent of the reflection functions (var_dump(), get_class_methods()) and the OO Reflection API.

    • rpj
      rpj over 14 years
      Yeah, great question. One of ObjC's biggest advantages over other similar languages is its amazing dynamic runtime system that allows you to do awesome things like this. Unfortunately, people rarely use it to its full potential, so kudos for teaching the SO community about it with your question.
    • Alexandre OS
      Alexandre OS almost 11 years
      I have created a lightweight library for dealing with reflection OSReflectionKit. Using this library you can simple call [the_thing fullDescription].
    • Patricia
      Patricia almost 10 years
      GREAT Question!! - Do you have any idea how to set the actual property once you have found it using Reflection? I have a question here: stackoverflow.com/q/25538890/1735836
  • Alan Storm
    Alan Storm over 14 years
    +1 for the information, just the kind of thing I was looking for. That said, I was hoping for a pre-built solution, as I'm new enough to the language that anything I'd quickly hack together might miss some major subtly of the language.
  • Dave DeLong
    Dave DeLong over 14 years
    +1 that's pretty much how I'd do it (make it an NSObject category). However, you have to free() your ivars, properties, and methods arrays, or else you're leaking them.
  • Felixyz
    Felixyz over 14 years
    Woops, forgot that. Put them in now. Thanks.
  • Dave DeLong
    Dave DeLong over 14 years
    If you're going to downvote an answer, please have the courtesy to leave a comment why.
  • Felixyz
    Felixyz over 14 years
    +1 very useful, although it only answers the question in part. Will you add a comment here when you release the library?
  • Alan Storm
    Alan Storm over 14 years
    +1 for good info, but sometimes it's faster to dump the state of an object at two points and diff the output than it is starting at the state of a program at a single point in time.
  • Samhan Salahuddin
    Samhan Salahuddin over 11 years
    Cant we see the values of the ivars ?
  • GeneralMike
    GeneralMike over 10 years
    ... And their types. I noticed you said it could be done, but you definitely seem to have a much better understanding of this than I do. Would you be able to update this at some point with the code to get the values and types for the ivars and properties?
  • Chuck Krutsinger
    Chuck Krutsinger almost 10 years
    With ARC, is it still necessary to free ivars, properties, and methods arrays etc.?
  • Patricia
    Patricia almost 10 years
    @Felixyz - This is GREAT!!!! Do you have any idea how to set the actual property once you have found it using Reflection? I have a question here: stackoverflow.com/q/25538890/1735836
  • Patricia
    Patricia almost 10 years
    @KendallHelmstetterGelner - GREAT INFO!! Do you have any idea how to set the actual property once you have found it using Reflection? I have a question here: stackoverflow.com/q/25538890/1735836
  • Patricia
    Patricia almost 10 years
    GREAT INFO!!! - Do you have any idea how to set the actual property once you have found it using Reflection? I have a question here: stackoverflow.com/q/25538890/1735836
  • SinisterMJ
    SinisterMJ almost 10 years
    @Lucy, any of the strings you get back using this technique can be used in [myObject setValue:myValue forKey:propertyName]. The technique I outlined above (using the property list) is reflection... you could also use the objc_property_t array to call the property methods directly, but setValue:forKey: is easier to use and works just as well.
  • Husein Behboudi Rad
    Husein Behboudi Rad over 7 years
    Can we stop some one to do this with our iOS library? I have a questions here: stackoverflow.com/questions/39807792/…