How can I get a list (in the form of an NSArray
or NSDictionary
) of a given object attributes 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 BOOL
and an NSData
object as attributes. Then I have several classes which extend this parent class, adding a lot of different attributes each.
想象一下下面的场景:我定义了一个父类,它只扩展NSObject,它持有一个NSString,一个BOOL和一个NSData对象作为属性。然后我有几个类,它们扩展了这个父类,每个类都添加了许多不同的属性。
Is there any way I could implement an instance method on the parent class that goes through the whole object and returns, say, an NSArray
of each of the (child) class attributes as NSStrings
that are not on the parent class, so I can later use these NSString
for KVC?
有什么方法可以在父类上实现一个实例方法,它遍历整个对象,然后返回,比如说,每个(子)类属性的NSArray,而不是父类的NSString,所以我以后可以用这些NSString来做KVC?
12 个解决方案
#1
106
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:
我自己也得到了答案。通过使用objc - 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函数,它主要取自一个苹果代码示例(现在不记得确切的源代码):
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 "@";
}
#2
70
@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
#3
28
@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时崩溃(实际上我有一个非常恼人的crashbug)。是有趣的调试^ ^)。我通过从属性中获取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
#4
8
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".
当我尝试使用ios3.2时,getPropertyType函数不能很好地处理属性描述。我在iOS文档中找到了一个例子:“Objective-C运行时编程指南:声明属性”。
Here is a revised code for property listing in iOS 3.2:
以下是ios3.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);
#5
7
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.
我发现,在模拟器中,玻利瓦的解决方案很好用,但在设备上,固定长度的子串会导致问题。我已经为这个设备上的工作编写了一个更加objective - c友好的解决方案。在我的版本中,我将属性的c字符串转换为一个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;
}
#6
5
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原语。它是ios8兼容的。这个类提供了三个类方法:
+ (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 specific to a subclass. Properties for its superclasses are not included.
返回特定于子类的所有可见属性的字典。它的超类的属性不包括在内。
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
#7
4
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的解决方案。因为我想要的——至少……但也不是没有一点尝试和错误。希望这些额外的信息能让某人免受痛苦。
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 Gist
is 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的“amazeballs”“内省……”他们肯定不会让简单的“看”“自己”“说话”变得很容易。
If you really want to go hog wild though.. check out.. class-dump, which is a mind-bogglingly insane way to peek into class headers of 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!
如果你真的想要疯狂的生活。看看. .类转储,这是一种令人难以置信的疯狂方法,可以窥视任何可执行文件的类头,等等…它提供了一个详细的检查您的类…,我个人,发现真正有用的——在许多,许多情况下。这就是为什么我开始寻找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
#8
4
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];
}
}
#9
3
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 API includes 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包括了为类获取ivar列表、方法列表或属性列表的函数(例如,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脚本,它只读取头文件,并查找您认为的类的“属性”。
#10
2
I was using function boliva provided, but apparently it stopped working with iOS 7. So now instead of static const char *getPropertyType(objc_property_t property) one can just use the following:
我使用了玻利维亚提供的功能,但显然它停止了使用ios7。因此,现在我们可以使用以下方法来代替静态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:@""];
}
#11
2
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);
}
#12
1
These answers are helpful, but I require more from that. All I want to do is to check whether the class type of a property is equal to that of an existing object. All the codes above are not capable of doing so, because: To get class name of an object, object_getClassName() returns texts like these:
这些答案是有帮助的,但我需要更多。我要做的是检查属性的类类型是否等于现有对象的类类型。上面所有的代码都不能这样做,因为:为了获得对象的类名,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:])
But if invoking getPropertyType(...) from above sample code, wit 4 objc_property_t structs of properties of a class defined like this:
但是,如果从上面的示例代码中调用getPropertyType(…),则可以使用如下定义的类的属性的4 objc_property_t struct:
@property (nonatomic, strong) NSArray* a0;
@property (nonatomic, strong) NSArray* a1;
@property (nonatomic, copy) NSNumber* n0;
@property (nonatomic, copy) NSValue* n1;
it returns strings respectively as following:
它分别返回字符串如下:
NSArray
NSArray
NSNumber
NSValue
So it is not able to determine whether an NSObject is capable of being the value of one property of the class. How to do that then?
因此,它不能确定NSObject是否能够作为类的一个属性的值。那么怎么做呢?
Here is my full sample code(function getPropertyType(...) is the same as above):
这里是我的完整样例代码(函数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;
}
#1
106
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:
我自己也得到了答案。通过使用objc - 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函数,它主要取自一个苹果代码示例(现在不记得确切的源代码):
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 "@";
}
#2
70
@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
#3
28
@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时崩溃(实际上我有一个非常恼人的crashbug)。是有趣的调试^ ^)。我通过从属性中获取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
#4
8
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".
当我尝试使用ios3.2时,getPropertyType函数不能很好地处理属性描述。我在iOS文档中找到了一个例子:“Objective-C运行时编程指南:声明属性”。
Here is a revised code for property listing in iOS 3.2:
以下是ios3.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);
#5
7
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.
我发现,在模拟器中,玻利瓦的解决方案很好用,但在设备上,固定长度的子串会导致问题。我已经为这个设备上的工作编写了一个更加objective - c友好的解决方案。在我的版本中,我将属性的c字符串转换为一个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;
}
#6
5
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原语。它是ios8兼容的。这个类提供了三个类方法:
+ (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 specific to a subclass. Properties for its superclasses are not included.
返回特定于子类的所有可见属性的字典。它的超类的属性不包括在内。
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
#7
4
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的解决方案。因为我想要的——至少……但也不是没有一点尝试和错误。希望这些额外的信息能让某人免受痛苦。
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 Gist
is 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的“amazeballs”“内省……”他们肯定不会让简单的“看”“自己”“说话”变得很容易。
If you really want to go hog wild though.. check out.. class-dump, which is a mind-bogglingly insane way to peek into class headers of 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!
如果你真的想要疯狂的生活。看看. .类转储,这是一种令人难以置信的疯狂方法,可以窥视任何可执行文件的类头,等等…它提供了一个详细的检查您的类…,我个人,发现真正有用的——在许多,许多情况下。这就是为什么我开始寻找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
#8
4
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];
}
}
#9
3
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 API includes 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包括了为类获取ivar列表、方法列表或属性列表的函数(例如,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脚本,它只读取头文件,并查找您认为的类的“属性”。
#10
2
I was using function boliva provided, but apparently it stopped working with iOS 7. So now instead of static const char *getPropertyType(objc_property_t property) one can just use the following:
我使用了玻利维亚提供的功能,但显然它停止了使用ios7。因此,现在我们可以使用以下方法来代替静态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:@""];
}
#11
2
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);
}
#12
1
These answers are helpful, but I require more from that. All I want to do is to check whether the class type of a property is equal to that of an existing object. All the codes above are not capable of doing so, because: To get class name of an object, object_getClassName() returns texts like these:
这些答案是有帮助的,但我需要更多。我要做的是检查属性的类类型是否等于现有对象的类类型。上面所有的代码都不能这样做,因为:为了获得对象的类名,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:])
But if invoking getPropertyType(...) from above sample code, wit 4 objc_property_t structs of properties of a class defined like this:
但是,如果从上面的示例代码中调用getPropertyType(…),则可以使用如下定义的类的属性的4 objc_property_t struct:
@property (nonatomic, strong) NSArray* a0;
@property (nonatomic, strong) NSArray* a1;
@property (nonatomic, copy) NSNumber* n0;
@property (nonatomic, copy) NSValue* n1;
it returns strings respectively as following:
它分别返回字符串如下:
NSArray
NSArray
NSNumber
NSValue
So it is not able to determine whether an NSObject is capable of being the value of one property of the class. How to do that then?
因此,它不能确定NSObject是否能够作为类的一个属性的值。那么怎么做呢?
Here is my full sample code(function getPropertyType(...) is the same as above):
这里是我的完整样例代码(函数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;
}