Общий метод описания Objective-C для печати значений ivar

В Objective-C обычно переопределяют -description методом, который печатает идентификатор объекта и имена/значения переменных экземпляра. Я хотел бы создать общий метод -description, который делает это путем самоанализа, а не вручную для каждого класса. Я хотел бы, чтобы вывод был примерно таким:

<ClassName: 0x??????, ivar1: value1, ivar2: value2, ivar3: value3, ...>

Также было бы неплохо отсортировать по имени переменной экземпляра (чтобы они всегда были в одном и том же порядке). Наконец, если бы это можно было отнести к категории, безопасно переопределяющей функциональность NSObject, это было бы идеально (но я вижу, что это не так просто, поскольку NSObject.m содержит предупреждение об использовании -[NSString stringWithFormat:] в -description).


person jrdioko    schedule 16.06.2011    source источник


Ответы (2)


Это большой вопрос! Поскольку я не нашел ничего подобного, я написал небольшую функцию, которая делает это за вас. Он заменяет - (NSString *)description из NSObject с помощью метода swizzling (да, я злой. МАХАХАХАХАХахахаха) и печатает ивары в том же порядке, в котором они появляются в классе (вы можете легко отредактировать их, чтобы отобразить их в алфавитном порядке).

НЕ ДЕЛАЙТЕ! забудьте позвонить NSObjectSwizzleDescription()!

.h файл:

@interface NSObject (JSObjectAdditions)
@end


void NSObjectSwizzleDescription();

.м файл:

#import <objc/objc.h>
#import "JSObject.h"

@implementation NSObject (JSObjectAdditions)

- (NSString *)verboseDescription
{
    NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: %p>", NSStringFromClass([self class]), self];

    uint32_t ivarCount;
    Ivar *ivars = class_copyIvarList([self class], &ivarCount);

    if(ivars)
    {
        [description appendString:@"\n{"];

        for(uint32_t i=0; i<ivarCount; i++)
        {
            Ivar ivar = ivars[i];
            const char *ivarType = ivar_getTypeEncoding(ivar);
            id ivarObject = object_getIvar(self, ivar);

            [description appendFormat:@"\n   %s: ", ivar_getName(ivar)];

            // Default signed data types
            if(strcmp(ivarType, "c") == 0)
            {
                char character = (char)ivarObject;
                [description appendFormat:@"'%c'", character];
            }
            else if(strcmp(ivarType, "i") == 0 || strcmp(ivarType, "l") == 0) // l is also 32 bit in the 64 bit runtime environment
            {
                int integer = (int)ivarObject;
                [description appendFormat:@"%i", integer];
            }
            else if(strcmp(ivarType, "s") == 0)
            {
                short shortVal = (short)ivarObject;
                [description appendFormat:@"%i", (int)shortVal];
            }
            else if(strcmp(ivarType, "q") == 0)
            {
                long long longVal = (long long)ivarObject;
                [description appendFormat:@"%l", longVal];
            }
            // Default unsigned data types
            else if(strcmp(ivarType, "C") == 0)
            {
                unsigned char chracter = (unsigned char)ivarObject;
                [description appendFormat:@"'%c'", chracter];
            }
            else if(strcmp(ivarType, "I") == 0 || strcmp(ivarType, "L") == 0)
            {
                unsigned int integer = (unsigned int)ivarObject;
                [description appendFormat:@"%u", integer];
            }
            else if(strcmp(ivarType, "S") == 0)
            {
                unsigned short shortVal = (unsigned short)ivarObject;
                [description appendFormat:@"%i", (int)shortVal];
            }
            else if(strcmp(ivarType, "Q") == 0)
            {
                unsigned long long longVal = (unsigned long long)ivarObject;
                [description appendFormat:@"%ll", longVal];
            }
            // Floats'n'stuff
            else if(strcmp(ivarType, "f") == 0)
            {
                float floatVal;
                memcpy(&floatVal, &ivarObject, sizeof(float));

                [description appendFormat:@"%f", floatVal];
            }
            else if(strcmp(ivarType, "d") == 0)
            {
                double doubleVal;
                memcpy(&doubleVal, &ivarObject, sizeof(double));

                [description appendFormat:@"%f", doubleVal];
            }
            // Boolean and pointer
            else if(strcmp(ivarType, "B") == 0)
            {
                BOOL booleanVal = (BOOL)ivarObject;
                [description appendFormat:@"%@", (booleanVal ? @"YES" : @"NO")];
            }
            else if(strcmp(ivarType, "v") == 0)
            {
                void *pointer = (void *)ivarObject;
                [description appendFormat:@"%p", pointer];
            }
            else if(strcmp(ivarType, "*") == 0 || strcmp(ivarType, ":") == 0) // SEL is just a typecast for a cstring
            {
                char *cstring = (char *)ivarObject;
                [description appendFormat:@"\"%s\"", cstring];
            }
            else if(strncmp(ivarType, "@", 1) == 0)
            {
                [description appendFormat:@"%@", ivarObject];
            }
            else if(strcmp(ivarType, "#") == 0)
            {
                Class objcClass = (Class)ivarObject;
                [description appendFormat:@"%s", class_getName(objcClass)];
            }
            else
                [description appendString:@"???"];
        }

        [description appendString:@"\n}"];
        free(ivars);
    }

    return description; 
}

@end


void NSObjectSwizzleDescription()
{
    Method origMethod = class_getInstanceMethod([NSObject class], @selector(description));
    Method newMethod = class_getInstanceMethod([NSObject class], @selector(verboseDescription));

    method_exchangeImplementations(origMethod, newMethod);
}
person JustSid    schedule 16.06.2011
comment
+1 и круто. Добавление всех строк к NSMutableArray и возврат [array componentsJoinedByString:@""] немного улучшит производительность за счет экономии выделений, хотя appendFormat NSMutableString не O(n^2), так что это не критично. - person orip; 07.01.2013

Я не знаю никакого кода, делающего это, но если бы он существовал, мне было бы интересно его отладить!

Я удивлен, что еще не нашел что-то подобное, классические методы просто работают, но было бы действительно неплохо иметь какой-то inspect метод, который, я думаю, возможен с использованием отражения, но, возможно, я ошибаюсь.

Я бы посоветовал взглянуть на этот интересный пост, связанный с тем, что вы ищете, и может стать основой для его реализации.

Еще одна интересная вещь — это NSLogger, который является хорошим инструментом для отладки.

person Vincent Guerci    schedule 16.06.2011