在 Objective-C 中获取对象属性列表
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/754824/
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
Get an object properties list in Objective-C
提问by boliva
How can I get a list (in the form of an NSArrayor NSDictionary) of a given object properties in Objective-C?
如何在 Objective-C 中获取给定对象属性的列表(以NSArray或的形式NSDictionary)?
Imagine the following scenario: I have defined a parent class which just extends NSObject, that holds an NSString, a BOOLand an NSDataobject as properties. Then I have several classes which extend this parent class, adding a lot of different properties each.
想象一下下面的场景:我定义了一个父类,它只是 extends NSObject,它包含一个NSString、 aBOOL和一个NSData对象作为属性。然后我有几个类扩展了这个父类,每个类都添加了很多不同的属性。
Is there any way I could implement an instance method on the parentclass that goes through the whole object and returns, say, an NSArrayof each of the (child) class properties as NSStringsthat are noton the parent class, so I can later use these NSStringfor KVC?
有什么方法可以在父类上实现一个实例方法,它遍历整个对象并返回,比如,NSArray每个(子)类属性中的一个,NSStrings因为它们不在父类上,所以我以后可以使用这些NSString对于 KVC?
回答by boliva
I just managed to get the answer myself. By using the Obj-C Runtime Library, I had access to the properties the way I wanted:
我只是设法自己得到了答案。通过使用 Obj-C 运行时库,我可以按照我想要的方式访问属性:
- (void)myMethod {
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([self class], &outCount);
for(i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
const char *propType = getPropertyType(property);
NSString *propertyName = [NSString stringWithCString:propName
encoding:[NSString defaultCStringEncoding]];
NSString *propertyType = [NSString stringWithCString:propType
encoding:[NSString defaultCStringEncoding]];
...
}
}
free(properties);
}
This required me to make a 'getPropertyType' C function, which is mainly taken from an Apple code sample (can't remember right now the exact source):
这需要我制作一个“getPropertyType”C 函数,该函数主要取自 Apple 代码示例(现在不记得确切的来源):
static const char *getPropertyType(objc_property_t property) {
const char *attributes = property_getAttributes(property);
char buffer[1 + strlen(attributes)];
strcpy(buffer, attributes);
char *state = buffer, *attribute;
while ((attribute = strsep(&state, ",")) != NULL) {
if (attribute[0] == 'T') {
if (strlen(attribute) <= 4) {
break;
}
return (const char *)[[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];
}
}
return "@";
}
回答by jpswain
@boliva's answer is good, but needs a little extra to handle primitives, like int, long, float, double, etc.
@boliva 的回答很好,但需要一些额外的东西来处理基元,如 int、long、float、double 等。
I built off of his to add this functionality.
我在他的基础上添加了这个功能。
// PropertyUtil.h
#import
@interface PropertyUtil : NSObject
+ (NSDictionary *)classPropsFor:(Class)klass;
@end
// PropertyUtil.m
#import "PropertyUtil.h"
#import "objc/runtime.h"
@implementation PropertyUtil
static const char * getPropertyType(objc_property_t property) {
const char *attributes = property_getAttributes(property);
printf("attributes=%s\n", attributes);
char buffer[1 + strlen(attributes)];
strcpy(buffer, attributes);
char *state = buffer, *attribute;
while ((attribute = strsep(&state, ",")) != NULL) {
if (attribute[0] == 'T' && attribute[1] != '@') {
// it's a C primitive type:
/*
if you want a list of what will be returned for these primitives, search online for
"objective-c" "Property Attribute Description Examples"
apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc.
*/
return (const char *)[[NSData dataWithBytes:(attribute + 1) length:strlen(attribute) - 1] bytes];
}
else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {
// it's an ObjC id type:
return "id";
}
else if (attribute[0] == 'T' && attribute[1] == '@') {
// it's another ObjC object type:
return (const char *)[[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];
}
}
return "";
}
+ (NSDictionary *)classPropsFor:(Class)klass
{
if (klass == NULL) {
return nil;
}
NSMutableDictionary *results = [[[NSMutableDictionary alloc] init] autorelease];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(klass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
const char *propType = getPropertyType(property);
NSString *propertyName = [NSString stringWithUTF8String:propName];
NSString *propertyType = [NSString stringWithUTF8String:propType];
[results setObject:propertyType forKey:propertyName];
}
}
free(properties);
// returning a copy here to make sure the dictionary is immutable
return [NSDictionary dictionaryWithDictionary:results];
}
@end
回答by felinira
@orange80's answer has one problem: It actually doesn't always terminate the string with 0s. This can lead to unexpected results like crashing while trying to convert it to UTF8 (I actually had a pretty annoying crashbug just because of that. Was fun debugging it ^^). I fixed it by actually getting an NSString from the attribute and then calling cStringUsingEncoding:. This works like a charm now. (Also works with ARC, at least for me)
@orange80 的回答有一个问题:它实际上并不总是以 0 终止字符串。这可能会导致意外的结果,例如在尝试将其转换为 UTF8 时崩溃(实际上我有一个非常烦人的崩溃错误。调试它很有趣^^)。我通过从属性中实际获取 NSString 然后调用 cStringUsingEncoding: 来修复它。这现在就像一个魅力。(也适用于 ARC,至少对我而言)
So this is my version of the code now:
所以这是我现在的代码版本:
// PropertyUtil.h
#import
@interface PropertyUtil : NSObject
+ (NSDictionary *)classPropsFor:(Class)klass;
@end
// PropertyUtil.m
#import "PropertyUtil.h"
#import <objc/runtime.h>
@implementation PropertyUtil
static const char *getPropertyType(objc_property_t property) {
const char *attributes = property_getAttributes(property);
//printf("attributes=%s\n", attributes);
char buffer[1 + strlen(attributes)];
strcpy(buffer, attributes);
char *state = buffer, *attribute;
while ((attribute = strsep(&state, ",")) != NULL) {
if (attribute[0] == 'T' && attribute[1] != '@') {
// it's a C primitive type:
/*
if you want a list of what will be returned for these primitives, search online for
"objective-c" "Property Attribute Description Examples"
apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc.
*/
NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];
return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
}
else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {
// it's an ObjC id type:
return "id";
}
else if (attribute[0] == 'T' && attribute[1] == '@') {
// it's another ObjC object type:
NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding];
return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
}
}
return "";
}
+ (NSDictionary *)classPropsFor:(Class)klass
{
if (klass == NULL) {
return nil;
}
NSMutableDictionary *results = [[NSMutableDictionary alloc] init];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(klass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
const char *propType = getPropertyType(property);
NSString *propertyName = [NSString stringWithUTF8String:propName];
NSString *propertyType = [NSString stringWithUTF8String:propType];
[results setObject:propertyType forKey:propertyName];
}
}
free(properties);
// returning a copy here to make sure the dictionary is immutable
return [NSDictionary dictionaryWithDictionary:results];
}
@end
回答by Chatchavan
When I tried with iOS 3.2, the getPropertyType function doesn't work well with the property description. I found an example from iOS documentation: "Objective-C Runtime Programming Guide: Declared Properties".
当我尝试使用 iOS 3.2 时,getPropertyType 函数不能很好地处理属性描述。我从 iOS 文档中找到了一个示例:“Objective-C 运行时编程指南:声明的属性”。
Here is a revised code for property listing in iOS 3.2:
这是 iOS 3.2 中属性列表的修订代码:
#import <objc/runtime.h>
#import <Foundation/Foundation.h>
...
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([UITouch class], &outCount);
for(i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
free(properties);
回答by Mitchell Vanderhoeff
I've found that boliva's solution works fine in the simulator, but on device the fixed length substring causes problems. I have written a more Objective-C-friendly solution to this problem that works on the device. In my version, I convert the C-String of the attributes to an NSString and perform string operations on it to get a substring of just the type description.
我发现 boliva 的解决方案在模拟器中运行良好,但在设备上,固定长度的子字符串会导致问题。我已经为这个问题编写了一个更适合 Objective-C 的解决方案,该解决方案适用于设备。在我的版本中,我将属性的 C-String 转换为 NSString 并对其执行字符串操作以获取仅类型描述的子字符串。
/*
* @returns A string describing the type of the property
*/
+ (NSString *)propertyTypeStringOfProperty:(objc_property_t) property {
const char *attr = property_getAttributes(property);
NSString *const attributes = [NSString stringWithCString:attr encoding:NSUTF8StringEncoding];
NSRange const typeRangeStart = [attributes rangeOfString:@"T@\""]; // start of type string
if (typeRangeStart.location != NSNotFound) {
NSString *const typeStringWithQuote = [attributes substringFromIndex:typeRangeStart.location + typeRangeStart.length];
NSRange const typeRangeEnd = [typeStringWithQuote rangeOfString:@"\""]; // end of type string
if (typeRangeEnd.location != NSNotFound) {
NSString *const typeString = [typeStringWithQuote substringToIndex:typeRangeEnd.location];
return typeString;
}
}
return nil;
}
/**
* @returns (NSString) Dictionary of property name --> type
*/
+ (NSDictionary *)propertyTypeDictionaryOfClass:(Class)klass {
NSMutableDictionary *propertyMap = [NSMutableDictionary dictionary];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(klass, &outCount);
for(i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
NSString *propertyName = [NSString stringWithCString:propName encoding:NSUTF8StringEncoding];
NSString *propertyType = [self propertyTypeStringOfProperty:property];
[propertyMap setValue:propertyType forKey:propertyName];
}
}
free(properties);
return propertyMap;
}
回答by Duncan Babbage
This implementation works with both Objective-C object types and C primitives. It is iOS 8 compatible. This class provides three class methods:
此实现适用于 Objective-C 对象类型和 C 原语。它与 iOS 8 兼容。这个类提供了三个类方法:
+ (NSDictionary *) propertiesOfObject:(id)object;
Returns a dictionary of all visible properties of an object, including those from all its superclasses.
返回对象的所有可见属性的字典,包括来自其所有超类的属性。
+ (NSDictionary *) propertiesOfClass:(Class)class;
Returns a dictionary of all visible properties of a class, including those from all its superclasses.
返回一个类的所有可见属性的字典,包括来自其所有超类的属性。
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
Returns a dictionary of all visible properties that are specificto a subclass. Properties for its superclasses are notincluded.
返回特定于子类的所有可见属性的字典。不包括其超类的属性。
One useful example of the use of these methods is to copy an object to a subclass instance in Objective-C without having to specify the properties in a copy method. Parts of this answer are based on the other answers to this question but it provides a cleaner interface to the desired functionality.
使用这些方法的一个有用示例是将对象复制到 Objective-C 中的子类实例,而无需在复制方法中指定属性。此答案的一部分基于此问题的其他答案,但它为所需功能提供了更清晰的界面。
Header:
标题:
// SYNUtilities.h
#import <Foundation/Foundation.h>
@interface SYNUtilities : NSObject
+ (NSDictionary *) propertiesOfObject:(id)object;
+ (NSDictionary *) propertiesOfClass:(Class)class;
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
@end
Implementation:
执行:
// SYNUtilities.m
#import "SYNUtilities.h"
#import <objc/objc-runtime.h>
@implementation SYNUtilities
+ (NSDictionary *) propertiesOfObject:(id)object
{
Class class = [object class];
return [self propertiesOfClass:class];
}
+ (NSDictionary *) propertiesOfClass:(Class)class
{
NSMutableDictionary * properties = [NSMutableDictionary dictionary];
[self propertiesForHierarchyOfClass:class onDictionary:properties];
return [NSDictionary dictionaryWithDictionary:properties];
}
+ (NSDictionary *) propertiesOfSubclass:(Class)class
{
if (class == NULL) {
return nil;
}
NSMutableDictionary *properties = [NSMutableDictionary dictionary];
return [self propertiesForSubclass:class onDictionary:properties];
}
+ (NSMutableDictionary *)propertiesForHierarchyOfClass:(Class)class onDictionary:(NSMutableDictionary *)properties
{
if (class == NULL) {
return nil;
}
if (class == [NSObject class]) {
// On reaching the NSObject base class, return all properties collected.
return properties;
}
// Collect properties from the current class.
[self propertiesForSubclass:class onDictionary:properties];
// Collect properties from the superclass.
return [self propertiesForHierarchyOfClass:[class superclass] onDictionary:properties];
}
+ (NSMutableDictionary *) propertiesForSubclass:(Class)class onDictionary:(NSMutableDictionary *)properties
{
unsigned int outCount, i;
objc_property_t *objcProperties = class_copyPropertyList(class, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = objcProperties[i];
const char *propName = property_getName(property);
if(propName) {
const char *propType = getPropertyType(property);
NSString *propertyName = [NSString stringWithUTF8String:propName];
NSString *propertyType = [NSString stringWithUTF8String:propType];
[properties setObject:propertyType forKey:propertyName];
}
}
free(objcProperties);
return properties;
}
static const char *getPropertyType(objc_property_t property) {
const char *attributes = property_getAttributes(property);
char buffer[1 + strlen(attributes)];
strcpy(buffer, attributes);
char *state = buffer, *attribute;
while ((attribute = strsep(&state, ",")) != NULL) {
if (attribute[0] == 'T' && attribute[1] != '@') {
// A C primitive type:
/*
For example, int "i", long "l", unsigned "I", struct.
Apple docs list plenty of examples of values returned. For a list
of what will be returned for these primitives, search online for
"Objective-c" "Property Attribute Description Examples"
*/
NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];
return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
}
else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {
// An Objective C id type:
return "id";
}
else if (attribute[0] == 'T' && attribute[1] == '@') {
// Another Objective C id type:
NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding];
return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
}
}
return "";
}
@end
回答by PakitoV
If someone is in the need of getting as well the properties inherited from the parent classes(as I did) here is some modification on "orange80" code to make it recursive:
如果有人需要获取从父类继承的属性(就像我所做的那样),这里是对“ orange80”代码进行一些修改以使其递归:
+ (NSDictionary *)classPropsForClassHierarchy:(Class)klass onDictionary:(NSMutableDictionary *)results
{
if (klass == NULL) {
return nil;
}
//stop if we reach the NSObject class as is the base class
if (klass == [NSObject class]) {
return [NSDictionary dictionaryWithDictionary:results];
}
else{
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(klass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
const char *propType = getPropertyType(property);
NSString *propertyName = [NSString stringWithUTF8String:propName];
NSString *propertyType = [NSString stringWithUTF8String:propType];
[results setObject:propertyType forKey:propertyName];
}
}
free(properties);
//go for the superclass
return [PropertyUtil classPropsForClassHierarchy:[klass superclass] onDictionary:results];
}
}
回答by Ans
You have three magic spells
你有三个魔法咒语
Ivar* ivars = class_copyIvarList(clazz, &count); // to get all iVars
objc_property_t *properties = class_copyPropertyList(clazz, &count); //to get all properties of a class
Method* methods = class_copyMethodList(clazz, &count); // to get all methods of a class.
Following piece of code can help you.
以下代码可以帮助您。
-(void) displayClassInfo
{
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* classInfo = [NSDictionary dictionaryWithObjectsAndKeys:
ivarArray, @"ivars",
propertyArray, @"properties",
methodArray, @"methods",
nil];
NSLog(@"%@", classInfo);
}
回答by Chuck
The word "attributes" is a little fuzzy. Do you mean instance variables, properties, methods that look like accessors?
“属性”这个词有点模糊。你的意思是实例变量、属性、方法看起来像访问器吗?
The answer to all three is "yes, but it's not very easy." The Objective-C runtime APIincludes functions to get the ivar list, method list or property list for a class (e.g., class_copyPropertyList()), and then a corresponding function for each type to get the name of an item in the list (e.g., property_getName()).
这三个问题的答案都是“是的,但这并不容易”。的Objective-C运行API包括函数来获得一类的实例变量列表,方法列表或属性列表(例如,class_copyPropertyList())中,然后对于每种类型的一个对应的功能来在列表中得到的项的名称(例如,property_getName())。
All in all, it can be kind of a lot of work to get it right, or at least a lot more than most people would want to do for what usually amounts to a really trivial feature.
总而言之,要想把它做好可能需要做很多工作,或者至少比大多数人想要做的工作要多得多,而这通常相当于一个非常微不足道的功能。
Alternatively, you could just write a Ruby/Python script that just reads a header file and looks for whatever you'd consider "attributes" for the class.
或者,您可以编写一个 Ruby/Python 脚本,该脚本仅读取头文件并查找您认为该类的“属性”的任何内容。
回答by Alex Gray
I was able to get @orange80's answer to work WITH ARC ENABLED… ... for what I wanted - at least... but not without a bit of trial and error. Hopefully this additional information may spare someone the grief.
我能够得到@orange80 的答案,以便在启用 ARC的情况下工作......对于我想要的 - 至少......但并非没有一点试验和错误。希望这些额外的信息可以让某人免于悲伤。
Save those classes he describes in his answer= as a class, and in your AppDelegate.h(or whatever), put #import PropertyUtil.h. Then in your...
将他在答案中描述的那些类保存为一个类,并在您的AppDelegate.h(或其他)中放入#import PropertyUtil.h. 那么在你...
- (void)applicationDidFinishLaunching:
(NSNotification *)aNotification {
method (or whatever)
…
方法(或其他)
…
PropertyUtil *props = [PropertyUtil new];
NSDictionary *propsD = [PropertyUtil classPropsFor:
(NSObject*)[gist class]];
NSLog(@"%@, %@", props, propsD);
…
The secret is to cast the instance variable of your class (in this Case my class is Gist, and my instance of Gistis gist) that you want to query... to NSObject… (id), etc, won't cut it.. for various, weird, esoteric reasons. This will give you some output like so…
秘诀是将您要查询的类的实例变量(在这种情况下,我的类是Gist,我的实例Gist是gist)转换为 NSObject...(id)等,不会将其剪切...用于各种奇怪的,深奥的原因。这会给你一些像这样的输出......
<PropertyUtil: 0x7ff0ea92fd90>, {
apiURL = NSURL;
createdAt = NSDate;
files = NSArray;
gistDescription = NSString;
gistId = NSString;
gitPullURL = NSURL;
gitPushURL = NSURL;
htmlURL = NSURL;
isFork = c;
isPublic = c;
numberOfComments = Q;
updatedAt = NSDate;
userLogin = NSString;
}
For all of Apple's unabashed / OCD bragging about ObjC's "amazeballs" "introspection... They sure don't make it very easy to perform this simple "look" "at one's self", "so to speak"..
对于所有苹果毫不掩饰的/强迫症吹嘘 ObjC 的“惊奇球”“内省......他们肯定不会让这种简单的“看”“自我”,“可以这么说”变得很容易。
If you really want to go hog wild though.. check out.. class-dump, which is a mind-bogglingly insane way to peek into class headersof ANY executable, etc… It provides a VERBOSE look into your classes… that I, personally, find truly helpful - in many, many circumstances. it is actually why I i started seeking a solution to the OP's question. here are some of the usage parameters.. enjoy!
如果你真的想疯狂......检查一下.. class-dump,这是一种令人难以置信的疯狂方式来查看任何可执行文件的类头等......它提供了对你的类的详细查看......我,就个人而言,发现真正有帮助 - 在很多很多情况下。这实际上是我开始寻求 OP 问题的解决方案的原因。这里是一些使用参数..享受!
-a show instance variable offsets
-A show implementation addresses
--arch <arch> choose a specific architecture from a universal binary (ppc, ppc64, i386, x86_64)
-C <regex> only display classes matching regular expression
-f <str> find string in method name
-I sort classes, categories, and protocols by inheritance (overrides -s)
-r recursively expand frameworks and fixed VM shared libraries
-s sort classes and categories by name
-S sort methods by name

