Objective-C Самоанализ/Отражение

Есть ли встроенный метод, функция, API, общепринятый способ и т.д. Для выгрузки содержимого экземпляра объекта в Objective-C, в частности в среде Apple Cocoa/Cocoa-Touch?

Я хочу быть в состоянии сделать что-то вроде

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

и отображать имена и значения переменных экземпляра объекта, а также любые методы, доступные для вызова во время выполнения. Идеально в удобном для чтения формате.

Для разработчиков, знакомых с PHP, я в основном ищу эквивалент функций отражения (var_dump(), get_class_methods()) и OO Reflection API.

Ответ 1

ОБНОВЛЕНИЕ: Любой, кто хочет делать такие вещи, может захотеть проверить оболочку Mike Ash ObjC для среды выполнения Objective-C.

Это более или менее то, как вы это сделаете:

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

Оттуда легко получить фактические значения свойств экземпляра, но вы должны проверить, являются ли они примитивными типами или объектами, поэтому я был слишком ленив, чтобы поместить его. Вы также можете выбрать сканирование цепочку наследования, чтобы получить все свойства, определенные для объекта. Тогда есть методы, определенные по категориям, и многое другое... Но почти все доступно.

Здесь выдержка из того, что приведенный выше код сбрасывает для 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
    );
}

Ответ 2

За исключением метода description (например,.toString() в Java), я не слышал о том, что было встроено, но создать его было бы непросто. В Objective-C Runtime Reference есть куча функций, которые вы можете использовать для получения информации об объектных переменных, методах, свойствах и т.д.

Ответ 3

Вот то, что я сейчас использую для автоматической печати переменных класса, в библиотеке для возможного публичного выпуска - он работает, сбрасывая все свойства из класса экземпляра, полностью восстанавливая дерево наследования. Благодаря KVC вам не нужно заботиться о том, является ли свойство примитивным типом или нет (для большинства типов).

// 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]]];
}

Ответ 4

Я сделал несколько настроек для кода Кендалла для печати значений свойств, которые мне очень пригодились. Я определил его как метод экземпляра вместо метода класса, как тот, который вызывает его рекурсия суперкласса. Я также добавил обработку исключений для не-KVO-совместимых свойств и добавил разрывы строк на выходе, чтобы упростить чтение (и 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;
}

Ответ 5

Честно говоря, правильный инструмент для этой работы - отладчик Xcode. Он имеет всю эту информацию, легко доступную визуально. Потратьте время, чтобы узнать, как его использовать, это действительно мощный инструмент.

Дополнительная информация:

Использование отладчика

Устаревшее руководство по отладке Xcode - заархивировано Apple

Об отладке с помощью Xcode - архивируется Apple

О LLDB и отладке - заархивировано Apple

Отладка с помощью GDB - архивируется Apple

Руководство по отладке SpriteKit - заархивировано Apple

Отладка тем программирования для Core Foundation - архивируется Apple

Ответ 6

Я сделал из этого cocoapod, https://github.com/neoneye/autodescribe

Я изменил код Кристофера Пикслая и сделал его категорией в NSObject, а также добавил unittest к нему. Вот как его использовать:

@interface TestPerson : NSObject

@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, strong) NSNumber *age;

@end

@implementation TestPerson

// empty

@end

@implementation NSObject_AutoDescribeTests

-(void)test0 {
    TestPerson *person = [TestPerson new];
    person.firstName = @"John";
    person.lastName = @"Doe";
    person.age = [NSNumber numberWithFloat:33.33];
    NSString *actual = [person autoDescribe];
    NSString *expected = @"firstName=John\nlastName=Doe\nage=33.33";
    STAssertEqualObjects(actual, expected, nil);
}

@end

Ответ 7

Я был перепутан с Самоанализом и Рефекцией раньше, поэтому получите некоторую информацию ниже.

Самоанализ - это возможность объекта проверить, какой он тип, или какой протокол он соответствует, или селектор, на который он может ответить. ObjC API, такие как isKindOfClass/isMemberOfClass/conformsToProtocol/respondsToSelector т.д.

Способность к усовершенствованию - это не только самоанализ, но и возможность не только получать информацию об объекте, но и управлять метаданными, свойствами и функциями объекта. такой как object_setClass может изменить тип объекта.