it-swarm.com.ru

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.

78
Alan Storm

ОБНОВЛЕНИЕ: Любой, кто хочет делать такие вещи, может захотеть проверить Оболочка Майка Эша 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
    );
}
112
Felixyz

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

12
Dave DeLong

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

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

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

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

с помощью отладчика

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

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

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

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

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

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

3
Colin Barrett

Я сделал Cocoapod из этого, 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
1
neoneye

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

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

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

0
Jone