bol*_*iva 109 attributes properties class introspection objective-c
如何在Objective-C中获取给定对象属性的列表(以NSArray
或的形式NSDictionary
)?
想象一下以下场景:我已经定义了一个只扩展的父类NSObject
,它将a NSString
,a BOOL
和一个NSData
对象作为属性.然后我有几个扩展这个父类的类,每个类都添加了许多不同的属性.
有没有什么方法可以在父类上实现一个遍历整个对象的实例方法,然后返回NSArray
每个(子)类属性的一个,因为NSStrings
它不在父类上,所以我以后可以使用这些NSString
对于KVC?
bol*_*iva 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);
}
Run Code Online (Sandbox Code Playgroud)
这要求我制作一个'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 "@";
}
Run Code Online (Sandbox Code Playgroud)
jps*_*ain 75
@ 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
Run Code Online (Sandbox Code Playgroud)
fel*_*ira 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
Run Code Online (Sandbox Code Playgroud)
小智 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);
Run Code Online (Sandbox Code Playgroud)
小智 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;
}
Run Code Online (Sandbox Code Playgroud)
此实现适用于Objective-C对象类型和C基元.它与iOS 8兼容.这个类提供了三个类方法:
+ (NSDictionary *) propertiesOfObject:(id)object;
Run Code Online (Sandbox Code Playgroud)
返回对象的所有可见属性的字典,包括来自其所有超类的属性.
+ (NSDictionary *) propertiesOfClass:(Class)class;
Run Code Online (Sandbox Code Playgroud)
返回类的所有可见属性的字典,包括来自其所有超类的属性.
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
Run Code Online (Sandbox Code Playgroud)
返回特定于子类的所有可见属性的字典.不包括其超类的属性.
使用这些方法的一个有用示例是将对象复制到Objective-C中的子类实例,而无需在复制方法中指定属性.本答案的部分内容基于此问题的其他答案,但它为所需功能提供了更清晰的界面.
标题:
// SYNUtilities.h
#import <Foundation/Foundation.h>
@interface SYNUtilities : NSObject
+ (NSDictionary *) propertiesOfObject:(id)object;
+ (NSDictionary *) propertiesOfClass:(Class)class;
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
@end
Run Code Online (Sandbox Code Playgroud)
执行:
// 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
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
63513 次 |
最近记录: |