How do I inspect a Class in Objective-C?

2019-04-07 22:29发布

问题:

Update I fixed up the code to eliminate duplication of overridden methods and track originator of property or method by implementing Mark's suggestion. Haven't tackled property types yet (will probably start with property_getAttributes() when I do). Also stripped out vestigial underscores.


Basically I need a way to remind myself what methods and properties are available on a given object without hopping all the way down the inheritance tree.

I've cooked up the following function but it leaves a bit to be desired:

#import <objc/runtime.h>

NSInteger inspectClass_alphabeticSort(id string1, id string2, void *reverse)
{
    if ((NSInteger *)reverse == NO)
    {
        return [string2 localizedCaseInsensitiveCompare:string1];
    }
    return [string1 localizedCaseInsensitiveCompare:string2];
}

void inspectClassStopAt(Class inspectedClass, Class stopClass)
{
    Class originalClass             = inspectedClass;
    NSString *originalClassString   = [NSString stringWithFormat:@"%@", originalClass];
    NSString *inheritancePath       = [NSString stringWithFormat:@"%@", originalClass];

    Method *methods;
    objc_property_t *properties;
    unsigned int i;
    unsigned int methodCount;
    unsigned int propertyCount;
    int reverseSort = NO;

    NSArray *sorted;
    NSArray *methodsAndPropertiesKeys;
    NSMutableDictionary * methodsAndProperties = [NSMutableDictionary dictionaryWithCapacity:10];

    NSString *inspectedClassString;
    NSString *methodOrPropertyName;
    while (inspectedClass != stopClass)
    {
        inspectedClassString = [NSString stringWithFormat:@"%@", inspectedClass];
        if (inspectedClass != originalClass)
        {
            inheritancePath = [inheritancePath stringByAppendingFormat:@" : %@", inspectedClass];
        }

        methods     = class_copyMethodList(inspectedClass, &methodCount);
        properties  = class_copyPropertyList(inspectedClass, &propertyCount);

        for (i=0; i<methodCount; i++)
        {
            methodOrPropertyName = [NSString stringWithFormat:@"-%s", sel_getName(method_getName(methods[i]))];

            if (![methodsAndProperties objectForKey:methodOrPropertyName])
            {
                [methodsAndProperties setObject:inspectedClassString forKey:methodOrPropertyName];
            }
        }

        for (i=0; i<propertyCount; i++)
        {
            methodOrPropertyName = [NSString stringWithFormat:@" %s", property_getName(properties[i])];

            if (![methodsAndProperties objectForKey:methodOrPropertyName])
            {
                [methodsAndProperties setObject:inspectedClassString forKey:methodOrPropertyName];
            }
        }

        inspectedClass = [inspectedClass superclass];
    }
    free(methods);
    free(properties);

    methodsAndPropertiesKeys = [methodsAndProperties allKeys];
    sorted = [methodsAndPropertiesKeys sortedArrayUsingFunction:inspectClass_alphabeticSort context:&reverseSort];

    NSLog(@"%@", inheritancePath);
    for (NSString *key in sorted)
    {
        if (![[methodsAndProperties objectForKey:key] isEqualToString:originalClassString])
        {
            NSLog(@"\t%@ (%@)", key, [methodsAndProperties objectForKey:key]);
        }
        else
        {
            NSLog(@"\t%@", key);
        }
    }
}

void inspectClass(Class inspectedClass)
{
    inspectClassStopAt(inspectedClass, [NSObject class]);
}

And some sample output from inspectClass([TextMap class]);:

TextMap : Surface
     position (Surface)
     size (Surface)
    -addChild: (Surface)
    -dealloc
    -init (Surface)
    -initWithFile:
    -position (Surface)
    -render
    -setPosition: (Surface)
    -setSize: (Surface)
    -setText:
    -size (Surface)
    -update (Surface)

回答1:

Seems like what you need is an NSMutableDictionary, keyed on the selector names. Add each selector name to the dictionary as a key, with the class name as the value. You can search the dictionary before adding a selector, to eliminate duplicates.



回答2:

A nice helper/wrapper for the c libraries provided with runtime.h is https://github.com/mikeash/MAObjCRuntime

I'm just starting to use it but it definitely looks promising.

As far as use case, mine is simply to provide an inspector to build a self wiring UI that can be attached to an object graph. That's alot of potential duplicate code reduction.