在Objective-C中获取对象属性列表

我如何得到Objective-C中给定对象属性的列表(以NSArrayNSDictionary的forms)?

想象一下以下场景:我定义了一个父类,它只是扩展NSObject ,它包含一个NSString ,一个BOOL和一个NSData对象作为属性。 然后我有几个扩展这个父类的类,每个类都添加了很多不同的属性。

有没有什么办法可以在类上实现一个实例方法,它贯穿整个对象,并将每个(子)类属性的NSArray作为不在父类中的NSStrings ,所以我可以稍后为KVC使用这些NSString

我只是设法自己得到答案。 通过使用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); } 

这需要我做一个'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 "@"; } 

@ boliva的答案是好的,但需要一些额外的处理基元,如int,long,float,double等

我build立了他的添加这个function。

 // 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 

@ orange80的答案有一个问题:它实际上并不总是以0结束string。 这可能会导致意想不到的结果,如试图将其转换为UTF8时崩溃(我真的有一个非常讨厌的crashbug正因为如此,很有趣的debugging它^^)。 我通过从属性中获取NSString来修复它,然后调用cStringUsingEncoding :. 这现在就像一个魅力。 (也适用于ARC,至less对我来说)

所以这是我现在的代码版本:

 // 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 

当我尝试使用iOS 3.2时,getPropertyType函数不适用于属性描述。 我从iOS文档中find了一个例子:“Objective-C运行时编程指南:声明的属性”。

以下是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); 

我发现boliva的解决scheme在模拟器中工作正常,但是在设备上固定长度的子string会导致问题。 我已经写了一个更适合Objective-C的解决scheme来解决这个问题。 在我的版本中,我将属性的C-String转换为NSString,并对其执行string操作,以获得types描述的子string。

 /* * @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; } 

这个实现与Objective-C对象types和C原语一起工作。 它与iOS 8兼容。 这个类提供了三个类的方法:

 + (NSDictionary *) propertiesOfObject:(id)object; 

返回一个包含所有超类的所有可见属性的字典。

 + (NSDictionary *) propertiesOfClass:(Class)class; 

返回类的所有可见属性的字典,包括所有超类的字典。

 + (NSDictionary *) propertiesOfSubclass:(Class)class; 

返回特定于子类的所有可见属性的字典。 包括其超类的属性。

使用这些方法的一个有用的例子是将对象复制到Objective-C中的子类实例,而不必在复制方法中指定属性 。 这个答案的部分是基于这个问题的其他答案,但它提供了一个更清晰的接口到所需的function。

标题:

 // SYNUtilities.h #import <Foundation/Foundation.h> @interface SYNUtilities : NSObject + (NSDictionary *) propertiesOfObject:(id)object; + (NSDictionary *) propertiesOfClass:(Class)class; + (NSDictionary *) propertiesOfSubclass:(Class)class; @end 

执行:

 // 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] != '@') { // AC 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 

我得到了@ orange80的答案,可以和ARC ENABLED一起工作……我想要的东西 – 至less……但不是没有一点试验和错误。 希望这些额外的信息可能会让人感到悲伤。

将他在answer =中描述的类保存为一个类,并在AppDelegate.h (或其他)中放入#import PropertyUtil.h 。 然后在你的…

 - (void)applicationDidFinishLaunching: (NSNotification *)aNotification { 

方法(或其他)

 PropertyUtil *props = [PropertyUtil new]; NSDictionary *propsD = [PropertyUtil classPropsFor: (NSObject*)[gist class]]; NSLog(@"%@, %@", props, propsD); … 

秘密是把你的类的实例variables( 在这种情况下,我的类是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; } 

对于所有苹果公司毫不掩饰的OCD吹嘘有关ObjC的“惊天动地”的反思……他们肯定不会轻易地在自己身上执行这个简单的“外观”,“可以这么说”。

如果你真的想疯了似的 .. ..检查出来.. class-dump ,这是一个令人难以置信的疯狂的方式来窥探任何可执行文件的类头等 …它提供了一个VERBOSE看你的课…我,在许多情况下,亲自find真正有用的帮助。 这实际上是我为什么开始寻求解决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 

如果有人需要从父类inheritance的属性 (就像我这样做),这里是对“ orange80 ”代码的一些修改,使其recursion:

 + (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]; } } 

“属性”一词有点模糊。 你的意思是实例variables,属性,方法看起来像访问?

三人的答案是“是的,但这不是很容易”。 Objective-C运行时API包括获取类的列表,方法列表或属性列表(例如, class_copyPropertyList() )的函数,然后为每个types获取列表中项目的名称(例如, property_getName() )。

总而言之,做出正确的决定可能有很多工作要做,或者至less比大多数人想做的事情要多得多,而这通常只是一个非常微不足道的function。

或者,您可以编写一个Ruby / Python脚本,它只读取一个头文件,并查找您认为该类的“属性”的内容。

我正在使用函数提供的玻利瓦尔,但显然它停止与iOS 7的工作。所以现在,而不是静态const char * getPropertyType(objc_property_t属性)可以只使用以下内容:

 - (NSString*) classOfProperty:(NSString*)propName{ objc_property_t prop = class_getProperty([self class], [propName UTF8String]); if (!prop) { // doesn't exist for object return nil; } const char * propAttr = property_getAttributes(prop); NSString *propString = [NSString stringWithUTF8String:propAttr]; NSArray *attrArray = [propString componentsSeparatedByString:@","]; NSString *class=[attrArray objectAtIndex:0]; return [[class stringByReplacingOccurrencesOfString:@"\"" withString:@""] stringByReplacingOccurrencesOfString:@"T@" withString:@""]; } 

你有三个魔法

 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. 

以下代码可以帮助你。

 -(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); } 

这些答案是有帮助的,但我需要更多。 我想要做的就是检查属性的类types是否与现有对象的类types相同。 所有上面的代码都无法这样做,因为:要获取对象的类名,object_getClassName()将返回如下所示的文本:

 __NSArrayI (for an NSArray instance) __NSArrayM (for an NSMutableArray instance) __NSCFBoolean (an NSNumber object initialized by initWithBool:) __NSCFNumber (an NSValue object initialized by [NSNumber initWithBool:]) 

但是,如果从上面的示例代码调用getPropertyType(…),那么定义一个类的属性的结构体为4个objc_property_t:

 @property (nonatomic, strong) NSArray* a0; @property (nonatomic, strong) NSArray* a1; @property (nonatomic, copy) NSNumber* n0; @property (nonatomic, copy) NSValue* n1; 

它分别返回string如下:

 NSArray NSArray NSNumber NSValue 

所以它不能确定一个NSObject是否能够作为这个类的一个属性的值。 那该怎么做?

这是我的完整示例代码(函数getPropertyType(…)与上面相同):

 #import <objc/runtime.h> @interface FOO : NSObject @property (nonatomic, strong) NSArray* a0; @property (nonatomic, strong) NSArray* a1; @property (nonatomic, copy) NSNumber* n0; @property (nonatomic, copy) NSValue* n1; @end @implementation FOO @synthesize a0; @synthesize a1; @synthesize n0; @synthesize n1; @end 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 ""; } int main(int argc, char * argv[]) { NSArray* a0 = [[NSArray alloc] init]; NSMutableArray* a1 = [[NSMutableArray alloc] init]; NSNumber* n0 = [[NSNumber alloc] initWithBool:YES]; NSValue* n1 = [[NSNumber alloc] initWithBool:NO]; const char* type0 = object_getClassName(a0); const char* type1 = object_getClassName(a1); const char* type2 = object_getClassName(n0); const char* type3 = object_getClassName(n1); objc_property_t property0 = class_getProperty(FOO.class, "a0"); objc_property_t property1 = class_getProperty(FOO.class, "a1"); objc_property_t property2 = class_getProperty(FOO.class, "n0"); objc_property_t property3 = class_getProperty(FOO.class, "n1"); const char * memberthype0 = getPropertyType(property0);//property_getAttributes(property0); const char * memberthype1 = getPropertyType(property1);//property_getAttributes(property1); const char * memberthype2 = getPropertyType(property2);//property_getAttributes(property0); const char * memberthype3 = getPropertyType(property3);//property_getAttributes(property1); NSLog(@"%s", type0); NSLog(@"%s", type1); NSLog(@"%s", type2); NSLog(@"%s", type3); NSLog(@"%s", memberthype0); NSLog(@"%s", memberthype1); NSLog(@"%s", memberthype2); NSLog(@"%s", memberthype3); return 0; }