Objective-C 内省/反思
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2299841/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Objective-C Introspection/Reflection
提问by Alan Storm
Is there a built in method, function, API, commonly accepted way, etc. to dump the contents of an instantiated object in Objective-C, specifically in Apple's Cocoa/Cocoa-Touch environment?
是否有内置的方法、函数、API、普遍接受的方式等来转储 Objective-C 中实例化对象的内容,特别是在 Apple 的 Cocoa/Cocoa-Touch 环境中?
I want to be able to do something like
我希望能够做类似的事情
MyType *the_thing = [[MyType alloc] init];
NSString *the_dump = [the_thing dump]; //pseudo code
NSLog("Dumped Contents: %@", the_dump);
and have the object's instance variable names and values displayed, along with any methods available to call at run time. Ideally in an easy to read format.
并显示对象的实例变量名称和值,以及可在运行时调用的任何方法。最好采用易于阅读的格式。
For developers familiar with PHP, I'm basically looking for the equivalent of the reflection functions (var_dump(), get_class_methods()) and the OO Reflection API.
对于熟悉 PHP 的开发人员,我基本上是在寻找等效的反射函数 ( var_dump(), get_class_methods()) 和 OO 反射 API。
回答by Felixyz
UPDATE:Anyone looking to do this kind of stuff might want to check out Mike Ash's ObjC wrapper for the Objective-C runtime.
更新:任何想做这类事情的人都可能想查看Mike Ash 的 ObjC 包装器,用于 Objective-C 运行时。
This is more or less how you'd go about it:
这或多或少是你如何去做的:
#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);
}
From there, it's easy to get the actual values of an instance's properties, but you have to check to see if they are primitive types or objects, so I was too lazy to put it in. You could also choose to scan the inheritance chain to get allthe properties defined on an object. Then there are methods defined on categories, and more... But almost everything is readily available.
从那里,很容易得到一个实例的属性的实际值,但是你必须检查它们是原始类型还是对象,所以我懒得放进去。 你也可以选择扫描继承链来获取对象上定义的所有属性。然后是在类别上定义的方法,等等......但几乎所有东西都是现成的。
Here's an excerpt of what the above code dumps for UILabel:
这是上面代码为 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
);
}
回答by Dave DeLong
Short of the descriptionmethod (like .toString() in Java), I haven't heard of one that was built in, but it wouldn't be too difficult to create one. The Objective-C Runtime Referencehas a bunch of functions you can use to get information about an object's instance variables, methods, properties, etc.
除了该description方法(如 Java 中的 .toString()),我还没有听说过内置的方法,但创建一个方法并不难。 Objective-C 运行时参考有一堆函数,你可以用来获取关于对象的实例变量、方法、属性等的信息。
回答by Kendall Helmstetter Gelner
Here's what I am currently using to automatically print class variables, in a library for eventual public release - it works by dumping all properties from the instance class all the way back up the inheritance tree. Thanks to KVC you don't need to care if a property is a primitive type or not (for most types).
这是我目前用于在最终公开发布的库中自动打印类变量的内容 - 它的工作原理是从实例类中转储所有属性,一直备份到继承树。感谢 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]]];
}
回答by Christopher Pickslay
I made a couple of tweaks to Kendall's code for printing property values, which came in very handy for me. I defined it as an instance method instead of a class method, as that's how the superclass recursion calls it. I also added exception handling for non-KVO-compliant properties, and added line breaks to the output to make it easier to read (and diff):
我对 Kendall 用于打印属性值的代码进行了一些调整,这对我来说非常方便。我将它定义为实例方法而不是类方法,因为这就是超类递归调用它的方式。我还为不符合 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;
}
回答by Colin Barrett
Honestly, the right tool for this job is Xcode's debugger. It has all this information easily accessible in a visual way. Take the time to learn how to use it, it's a really powerful tool.
老实说,适合这项工作的工具是 Xcode 的调试器。它以可视方式轻松访问所有这些信息。花点时间学习如何使用它,它是一个非常强大的工具。
More information:
更多信息:
Outdated Xcode Debugging Guide- archived by Apple
过时的 Xcode 调试指南- 由 Apple 存档
About Debugging with Xcode- archived by Apple
关于使用 Xcode 调试- 由 Apple 存档
About LLDB and Debugging- archived by Apple
关于 LLDB 和调试- 由 Apple 存档
Debugging with GDB- archived by Apple
使用 GDB 调试- 由 Apple 存档
SpriteKit Debugging Guide- archived by Apple
SpriteKit 调试指南- 由 Apple 存档
Debugging Programming Topics for Core Foundation- archived by Apple
Core Foundation 的调试编程主题- 由 Apple 存档
回答by neoneye
I have made cocoapod out of this, https://github.com/neoneye/autodescribe
我已经用这个制作了可可豆,https://github.com/neoneye/autodescribe
I have modified Christopher Pickslay's code and made it a category on NSObject and also added a unittest to it. Here is how to use it:
我修改了 Christopher Pickslay 的代码,并将其作为 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
回答by Jone
I am confused with Introspection and Refection before, so get some information below.
我之前对 Introspection 和 Refection 感到困惑,所以在下面获取一些信息。
Introspection is the capability for object to check which type it is, or protocol it conformed, or selector it can response. The objc api such as isKindOfClass/isMemberOfClass/conformsToProtocol/respondsToSelectoretc.
自省是对象检查它是哪种类型的能力,或者它符合的协议,或者它可以响应的选择器。所述objc API诸如isKindOfClass/ isMemberOfClass/ conformsToProtocol/respondsToSelector等
Refection capability is further than Introspection,It's not only can get object information but also can operation object meta-data, properties and functions. such as object_setClasscan modify object type.
Refect 能力比Introspection 更进一步,不仅可以获取对象信息,还可以操作对象元数据、属性和函数。比如object_setClass可以修改对象类型。

