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


109

我如何获取列表(以 NSArrayNSDictionary在Objective-C中获得给定对象属性或)?

想象以下情况:我定义了一个仅扩展了的父类,其中NSObject包含an NSString,a BOOL和anNSData对象作为属性。然后,我有几个扩展该父类的类,每个类都添加了许多不同的属性。

我有什么办法可以在类上实现遍历整个对象并返回例如NSArray每个(子)类属性的实例方法,因为NSStrings它们不在父类上,所以我以后可以使用它们NSString对于KVC?

Answers:


116

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

5
+1除外,这会在原语(例如int)上出错。请在下面查看我的答案以获取与此功能相比稍有增强的版本。
jpswain 2011年

1
出于正确性考虑,[NSString stringWithCString:]不推荐使用[NSString stringWithCString:encoding:]
zekel 2012年

4
应该导入objc运行时头文件#import <objc / runtime.h>它在ARC上有效。
大金(Dae KIM)2012年

是使用Swift完成的方式。
拉米斯

76

@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

1
您打算#import <Foundation/Foundation.h>放在.h文件的顶部吗?
安德鲁

2
[NSString的stringWithUTF8String:propType]无法分析“propType为const char * “的NSNumber \ X94 \ xfdk;”,并返回一个字符串为零......不知道为什么它是这样一个奇怪的NSNumber MB,因为ActiveRecord的。?
Dumoko

高超!非常感谢。
阿西克·阿卜杜拉

这绝对是完美的!
Pranoy C '18

28

@ 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您能否提供一个示例来说明我提供的代码存在的问题?我只是好奇地看到它。
jpswain

@ orange80好吧,AFAIR数据永远不会以零结尾。如果是这样,这只是偶然发生。我可能是错的。其他新闻:我仍然在运行此代码,并且运行可靠:p
felinira

@ orange80我在尝试从Google的IMA广告库中的IMAAdRequest上调用您的版本时遇到了此问题。farthen的解决方案解决了它。
Christopher Pickslay 2014年

谢谢。这在iOS7中对我有用,而前两个答案没有。+1所有3
ChrisH

这是唯一对我有用的答案。其他所有事情都给我像“ NSString \ x8d \ xc0 \ xd9”这样的属性类型的怪异之处,大概是因为char *大小设置已关闭
Brian Colavito 2014年

8

当我尝试使用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);

7

我发现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 const typeRangeStart = [attributes rangeOfString:@“ T @ \”“];上引发EXC_BAD_ACCESS异常; //字符串类型的开始
Adam Mendoza

6

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

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

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

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

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

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

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

使用这些方法的一个有用示例是 将对象复制到Objective-C中的子类实例,而无需在复制方法中指定属性。该答案的某些部分基于该问题的其他答案,但它为所需功能提供了更简洁的界面。

标头:

//  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];
亚当·门多萨

4

如果有人需要很好地获取从父类继承的属性(就像我所做的那样),则可以对“ 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];

    }
}

1
我们不能将其设为一个类别并使用它扩展NSObject,以便将此功能内置到NSObject子级的每个类中吗?
Alex Zavatone 2013年

这听起来像是个好主意,如果我能找到时间,可以使用该选项更新答案。
PakitoV 2013年

完成此操作后,如果有时间,我将添加一个方法转储。现在是时候在每个NSObject上获得真实的对象属性和方法自省了。
Alex Zavatone 2013年

我也一直在努力增加价值的输出,但似乎对于某些结构(矩形),类型是属性的实际值。tableViewController的caretRect和viewController结构中的其他未签名int的情况就是这种情况,即返回c或f作为与Objective-C Runtime文档冲突的类型。显然,此处需要完成更多工作才能完成此工作。 developer.apple.com/library/mac/documentation/cocoa/conceptual/...
亚历Zavatone

我正在看一下,但是有一个我无法解决的问题,要使其递归,我需要调用超类的方法(例如上一代码的最后一行),因为NSObject是无法在类别内工作的根类。因此,没有递归的可能... :(不知道NSObject中的类别是否可以继续使用...
PakitoV 2013年

3

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

这三个问题的答案都是“是的,但这并不容易”。的Objective-C运行API包括函数来获得一类的实例变量列表,方法列表或属性列表(例如,class_copyPropertyList())中,然后对于每种类型的一个对应的功能来在列表中得到的项的名称(例如,property_getName())。

总而言之,要想正确使用它可能需要做很多工作,或者至少比大多数人想做的要多得多,因为通常这只是一个琐碎的功能。

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


嗨,查克,感谢您的回复。我所说的“属性”确实是一个类属性。我已经通过使用Obj-C运行时库来完成我想要的事情。使用脚本来解析头文件将无法满足我在运行时所需的功能。
boliva

3

我能够得到@ orange80的答案,以便与ARC ENABLED一起工作…………我想要的-至少……但并非没有尝试和错误。希望这些附加信息可以使某人免于悲痛。

他在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,我的Gistis的实例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的“迷宫”自夸的自欺欺人的OCD而言,“自省……他们肯定不会轻易地以这种“眼神”来表现这种简单的“表情”。

如果您真的想狂野地走..签出.. 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

3

你有三个魔法

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

2

我使用的是提供的功能boliva,但显然它已停止与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:@""];
}

你是我的英雄。我仍然必须手动纠正某些问题(由于某些原因,BOOL会以“ Tc”的形式出现),但这实际上使我可以使事情重新工作。
Harpastum

基元具有自己的类型,“ @”表示对象,并且其后的类名出现在引号之间。唯一的例外是id,它被简单地编码为“ T @”
Mihai Timar 2014年

2

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

  1. 使对象符合Encodable协议

    class ExampleObj: NSObject, Encodable {
        var prop1: String = ""
        var prop2: String = ""
    }
  2. 创建扩展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
    }
  3. 调用toDictionary对象实例和访问keys属性。

    let exampleObj = ExampleObj()
    exampleObj.toDictionary()?.keys
  4. 瞧!像这样访问您的属性:

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

1

这些答案很有帮助,但是我需要更多的答案。我要做的就是检查属性的类类型是否等于现有对象的类类型。上面的所有代码均无法执行此操作,因为:为了获取对象的类名,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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.