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
может изменить тип объекта.