Цель 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()
) и API отражения OO.
6 ответов:
обновление: любой, кто хочет сделать такие вещи, может захотеть проверить оболочка 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 ); }
меньше
description
способ (как .toString () в Java), я не слышал о том, который был встроен, но это не было бы слишком трудно создать один. Ссылка На Время Выполнения Objective-C имеет кучу функций, которые можно использовать для получения информации о переменных экземпляра объекта, методов, свойств и т. д.
вот что я в настоящее время использую для автоматической печати переменных класса в библиотеке для возможного публичного выпуска - он работает, сбрасывая все свойства из класса экземпляра вплоть до дерева наследования. Благодаря 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]]]; }
Я сделал пару настроек для кода Кендалла для печати значений свойств, которые мне очень пригодились. Я определил его как метод экземпляра вместо метода класса, поскольку именно так рекурсия суперкласса вызывает его. Я также добавил обработку исключений для свойств, не совместимых с 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; }
честно говоря, правильным инструментом для этой работы является отладчик Xcode. Он имеет всю эту информацию легко доступны в визуальном виде. Потратьте время, чтобы узнать, как его использовать, это действительно мощный инструмент. Дополнительная информация: Руководство По Отладке Xcode.
Я сделал 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