Objective-C Самоанализ / Отражение - PullRequest
78 голосов
/ 20 февраля 2010

Есть ли встроенный метод, функция, 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.

Ответы [ 7 ]

112 голосов
/ 20 февраля 2010

ОБНОВЛЕНИЕ: Любой, кто хочет заняться этим, может захотеть проверить Обертку Майка Эша 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
    );
}
12 голосов
/ 20 февраля 2010

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

8 голосов
/ 21 февраля 2010

Вот то, что я в настоящее время использую для автоматической печати переменных класса, в библиотеке для возможного публичного выпуска - она ​​работает, сбрасывая все свойства из класса экземпляра вплоть до резервного копирования дерева наследования.Благодаря 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 голосов
/ 05 января 2011

Я внес несколько изменений в код Кендалла для печати значений свойств, которые мне очень пригодились. Я определил его как метод экземпляра вместо метода класса, так как именно так его называет рекурсия суперкласса. Я также добавил обработку исключений для не совместимых с KVO свойств и добавил разрывы строк в вывод, чтобы было легче читать (и различать):

-(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;
}
3 голосов
/ 21 февраля 2010

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

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

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

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

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

Об LLDB и отладке - архивируется Apple

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

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

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

1 голос
/ 05 октября 2012

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

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

@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
0 голосов
/ 19 мая 2019

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

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

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

...