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

Posted

技术标签:

【中文标题】在 Objective-C 中获取对象属性列表【英文标题】:Get an object properties list in Objective-C 【发布时间】:2010-10-19 18:46:50 【问题描述】:

如何在 Objective-C 中获取给定对象属性的列表(以NSArrayNSDictionary 的形式)?

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

有什么方法可以在 parent 类上实现一个实例方法,该方法遍历整个对象并返回,例如,每个(子)类属性的NSArray 作为@ 987654328@ 不在父类上,所以我以后可以将这些NSString 用于 KVC?

【问题讨论】:

【参考方案1】:

“属性”这个词有点模糊。您是指看起来像访问器的实例变量、属性、方法吗?

这三个问题的答案都是“是的,但这并不容易”。 Objective-C runtime API 包括用于获取类的 ivar 列表、方法列表或属性列表的函数(例如,class_copyPropertyList()),然后是每种类型的相应函数以获取列表中项目的名称(例如,@ 987654323@)。

总而言之,要做到这一点可能需要做很多工作,或者至少比大多数人想要做的事情要多得多,因为这通常相当于一个非常微不足道的功能。

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

【讨论】:

嗨查克,感谢您的回复。我所说的“属性”确实是指类属性。通过使用 Obj-C 运行时库,我已经设法完成了我想要的。使用脚本来解析头文件对于我在运行时需要的东西不起作用。【参考方案2】:

我自己设法得到了答案。通过使用 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 "@";

【讨论】:

+1 除非这会在原语上出错,例如 int。请在下面查看我的答案,以了解同一事物的略微增强版本。 为了正确起见,[NSString stringWithCString:][NSString stringWithCString:encoding:] 取代。 应该导入 objc 运行时标头 #import 它适用于 ARC。 Here 是如何使用 Swift 完成的。【参考方案3】:

当我尝试使用 ios 3.2 时,getPropertyType 函数不能很好地与属性描述配合使用。我从 iOS 文档中找到了一个示例:“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);

【讨论】:

【参考方案4】:

@boliva 的回答很好,但需要一些额外的东西来处理原语,如 int、long、float、double 等。

我在他的基础上添加了这个功能。

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

【讨论】:

您是否打算将#import &lt;Foundation/Foundation.h&gt; 放在 .h 文件的顶部? [NSString stringWithUTF8String:propType] 无法解析 "propType const char * "NSNumber\x94\xfdk;" 并返回一个 nil 字符串...不知道为什么它是一个奇怪的 NSNumber . Mb 因为 ActiveRecord? 太棒了!非常感谢。 这绝对是完美的!【参考方案5】:

我能够得到@orange80 的回答,启用 ARC……我想要的 - 至少……但并非没有一点试错。希望这些额外的信息可以减轻别人的痛苦。

将those classes he describes in his answer = 保存为一个类,并在您的AppDelegate.h(或其他)中输入#import PropertyUtil.h。然后在你的...

- (void)applicationDidFinishLaunching:
         (NSNotification *)aNotification 

方法(或其他)

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

秘诀是转换你想要查询的类的实例变量(在这种情况下,我的类是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;

对于所有苹果公司毫不掩饰/强迫症吹嘘 ObjC 的“惊奇球”“内省......他们肯定不会让执行这种简单的“看”“自我”,“可以这么说”变得很容易......

如果你真的想去疯狂..看看..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

【讨论】:

【参考方案6】:

我发现 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;

【讨论】:

这会在 NSRange 上抛出 EXC_BAD_ACCESS 异常 const typeRangeStart = [attributes rangeOfString:@"T@\""]; // 字符串类型的开始【参考方案7】:

@orange80 的回答有一个问题:它实际上并不总是以 0 结束字符串。这可能会导致意外结果,例如在尝试将其转换为 UTF8 时崩溃(正因为如此,我实际上遇到了一个非常烦人的崩溃错误。调试它很有趣 ^^)。我通过从属性中实际获取一个 NSString 然后调用 cStringUsingEncoding: 来修复它。这现在就像一个魅力。 (也适用于 ARC,至少对我而言)

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

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

【讨论】:

@farthen 你能提供一个例子来演示我提供的代码的问题吗?我只是想看看它。 @orange80 好吧,AFAIR 数据永远不会以零结尾。如果是这样,这只会发生在意外情况下。不过我可能是错的。在其他新闻中:我仍然在运行此代码并且它运行稳定:p @orange80 我在尝试从谷歌的 IMA 广告库中调用您的 IMAAdRequest 版本时遇到了这个问题。 farthen 的解决方案解决了它。 谢谢。当前两个答案没有时,这在 iOS7 中对我有用。所有 3 人 +1。 这是唯一对我有用的答案。其他所有东西都给了我类似“NSString\x8d\xc0\xd9”属性类型的怪异,大概是因为 char* 大小已关闭【参考方案8】:

我使用的是提供的 boliva 函数,但显然它在 iOS 7 中停止工作。所以现在可以使用以下命令代替 static const char *getPropertyType(objc_property_t property):

- (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:@""];

【讨论】:

你是我的英雄。我仍然需要手动更正一些事情(由于某种原因,BOOL 会显示为“Tc”),但这实际上让我可以让事情再次正常运行。 基元有自己的类型,“@”表示对象,在它之后,类名出现在引号之间。唯一的例外是 id,它被简单地编码为“T@”【参考方案9】:

如果有人也需要从父类继承的属性(就像我做的那样)这里是对“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];

    

【讨论】:

我们不能把它变成一个类别并用它扩展 NSObject,这样这个功能就内置到每个 NSObject 的子类中了吗? 这听起来是个好主意,如果我能找到时间将使用该选项更新答案。 一旦你完成了,我会在有时间的时候添加一个方法转储。是时候在每个 NSObject 之上获得真正的对象属性和方法自省了。 我也一直在努力增加值输出,但似乎对于某些结构(矩形),类型是属性的实际值。 tableViewController 的 caretRect 和 viewController 结构中的其他无符号整数就是这种情况,返回 c 或 f 作为与 Objective-C 运行时文档冲突的类型。显然,这里需要做更多的工作才能完成。 developer.apple.com/library/mac/documentation/cocoa/conceptual/… 我正在查看,但有一个问题我无法解决,为了使其递归,我需要调用超类的方法(如上一个代码的最后一行),因为 NSObject 是不能在类别中工作的根类。所以不可能有递归...... :( 不确定 NSObject 中的一个类别是否是可行的方法......【参考方案10】:

此实现适用于 Objective-C 对象类型和 C 原语。它与 iOS 8 兼容。该类提供了三个类方法:

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

返回对象所有可见属性的字典,包括来自其所有超类的属性。

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

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

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

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

使用这些方法的一个有用示例是copy an object to a subclass instance in Objective-C without having to specify the properties in a copy method。此答案的部分内容基于此问题的其他答案,但它为所需功能提供了更简洁的界面。

标题:

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

【讨论】:

我在这一行得到一个 EXC_BAD_ACCESS 异常 NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];【参考方案11】:

这些答案很有帮助,但我需要更多。我要做的就是检查属性的类类型是否等于现有对象的类类型。上面的所有代码都不能这样做,因为: 要获取对象的类名,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;

分别返回如下字符串:

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;

【讨论】:

【参考方案12】:

你有三个魔法咒语

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

【讨论】:

【参考方案13】:

对于 Swift 旁观者,您可以通过使用 Encodable 功能获得此功能。我将解释如何:

    使您的对象符合Encodable 协议

    class ExampleObj: NSObject, Encodable 
        var prop1: String = ""
        var prop2: String = ""
    
    

    Encodable 创建扩展以提供toDictionary 功能

     public func toDictionary() -> [String: AnyObject]? 
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
        guard let data =  try? encoder.encode(self),
              let json = try? JSONSerialization.jsonObject(with: data, options: .init(rawValue: 0)), let jsonDict = json as? [String: AnyObject] else 
            return nil
        
        return jsonDict
    
    

    在您的对象实例上调用 toDictionary 并访问 keys 属性。

    let exampleObj = ExampleObj()
    exampleObj.toDictionary()?.keys
    

    瞧!像这样访问您的属性:

    for k in exampleObj!.keys 
        print(k)
    
    // Prints "prop1"
    // Prints "prop2"
    

【讨论】:

以上是关于在 Objective-C 中获取对象属性列表的主要内容,如果未能解决你的问题,请参考以下文章

一个Objective-C对象如何进行内存布局?(考虑有父类的情况)

TypeScript:标头/值列表->列出ob对象[关闭]

IOS/objective-c/core-data:如何从相关实体获取属性

python测试一

PHP检查对象或类中是不是存在属性

Observer(__ob__: Observer) 对象添加属性