2

Since property named "age" would always have a selector named "age" as well, I could use respondsToSelector as this question suggests and that will tell me if a particular selector exists at runtime in any given object.

If a property named "age" exists, I can verify that. How could I know if that selector (the read method for that property) returns an object (id) or non-object (int)?

Is such type determination possible at runtime, or is the Objective-C way to always assume that someone implemented that method using the type I'm hoping it used, or can I also verify the return type?

This is using the latest Objective-C version (LLVM 4.1) in XCode 4.5.

Update: This is the utility-category-on-NSObject that I came up with:

   - (NSString*) propertyType: (NSString*)propname
{
    objc_property_t aproperty = class_getProperty([self class],  [propname cStringUsingEncoding:NSASCIIStringEncoding] ); // how to get a specific one by name.
    if (aproperty)
    {
     char * property_type_attribute = property_copyAttributeValue(aproperty, "T");
     NSString *result = [NSString stringWithUTF8String:property_type_attribute];
     free(property_type_attribute);
     return result;
    }
    else
        return nil;
}

While looking into this question I also wrote this handy-dandy utility method that can list all the properties on this object:

- (NSArray*) properties;
{
    NSMutableArray *results = [NSMutableArray array];
    @autoreleasepool {

        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 * aname=property_getName(property);
            [results addObject:[NSString stringWithUTF8String:aname]];
            //const char * attr= property_getAttributes(property);
            //[results addObject:[NSString stringWithUTF8String:attr]];

        }
        if (properties) {
            free(properties);
        }

  } // end of autorelease pool.
  return results;
}
Community
  • 1
  • 1
Warren P
  • 65,725
  • 40
  • 181
  • 316
  • I think you need to add if (properties) free(properties); to your handy-dandy category utility method – DonnaLea May 21 '14 at 17:59
  • Probably right. I haven't done any objective-C coding in some months and feeling rather rusty. I'm not 100% sure you're right, either. :-) Some Objective-C runtime reading might be required for me to figure that out either way. – Warren P May 21 '14 at 20:12
  • I'd written my own category before doing the same thing and had to fix this up for myself too. Only just noticed it now looking at the documentation a bit closer after seeing Carl's answer mentioning free() for using property_copyAttributeValue – DonnaLea May 21 '14 at 23:36
  • Okay good, I'll take your word for it. Can you do the code edit please (edit my post) and I'll approve it if your rep is too low – Warren P May 22 '14 at 00:58
  • Thanks for the edit, DonnaLea! – Warren P May 29 '14 at 03:15

2 Answers2

3

You could use class_copyPropertyList to get a list of properties declared in a class.

class_copyPropertyList

Describes the properties declared by a class.

And then property_getAttributes:

property_getAttributes

Returns the attribute string of an property.

Here you can find some more concrete hints and examples.

As a side note, the following statement:

Since property named "age" would always have a selector named "age" as well

is not correct, since a property can have custom getter and/or setter:

@property (nonatomic, getter=isImmediate) BOOL immediate;

EDIT:

Some sample code I found in another S.O. post:

const char * type = property_getAttributes(class_getProperty([self class], "myPropertyName"));
NSString * typeString = [NSString stringWithUTF8String:type];
NSArray * attributes = [typeString components separatedByString:@","];
NSString * typeAttribute = [attributes objectAtIndex:0];
NSString * propertyType = [typeAttribute substringFromIndex:1];
const char * rawPropertyType = [propertyType UTF8String];

if (strcmp(rawPropertyType, @encode(float)) == 0) {
 //it's a float
} else if (strcmp(rawPropertyType, @encode(int)) == 0) {
 //it's an int
} else if (strcmp(rawPropertyType, @encode(id)) == 0) {
 //it's some sort of object
} else ....
Community
  • 1
  • 1
sergio
  • 68,819
  • 11
  • 102
  • 123
  • I thought it was part of the "contract" that a property "abc" must have a read-selector "abc". Not so? – Warren P Jan 14 '13 at 21:33
  • you can override that "contract" and specify the name you want. look at the example. I don't think that in that case you wold also have a `immediate` selector beside `isImmediate`. – sergio Jan 14 '13 at 21:36
  • 1
    So property_getAttributes returns a lot of stuff as (char*), is there a direct way to check for "type : BOOL"? – Warren P Jan 14 '13 at 21:44
  • I don´t think so, but I found some sample code that might help... check my edit. – sergio Jan 14 '13 at 21:58
  • From that linked question it seems like runtime-boxing might be more idiomatic, than doing this with brute force: http://stackoverflow.com/questions/3497625/in-objective-c-determine-if-a-property-is-an-int-float-double-nsstring-nsdat – Warren P Jan 14 '13 at 22:02
2

One approach you can take, assuming you know the property name already, is to use the class_getProperty function. You can also use the property_copyAttributeValue() function to get just a particular attribute by name:

objc_property_t number_property = class_getProperty([MyClass class], "number");
char *number_property_type_attribute = property_copyAttributeValue(number_property, "T");
NSLog(@"number property type attribute = %s", number_property_type_attribute);

Will log:

2013-01-14 14:45:37.382 RuntimeFun[61304:c07] number property type attribute = i

Assuming MyClass looks something like:

@interface MyClass : NSObject
@property (nonatomic) int number;
@end

@implementation MyClass
@end

One you have your type attribute string, you can then compare it to the various Objective-C type encodings. Once you're done with your comparison, be sure to call free() on your attribute string.

Carl Veazey
  • 18,392
  • 8
  • 66
  • 81
  • 1
    `property_copyAttributeValue(x,"T")` was exactly what I was looking for. – Warren P Jan 14 '13 at 22:13
  • 1
    I wrote this up as a utility-category-thingy in my question, along with the `free(attribute)`. – Warren P Jan 14 '13 at 22:20
  • @WarrenP great! One thing to keep in mind, you sort of hint at this in your question, is to check for the actual implementation of the property. You can check if custom getters or setters are used by checking the "G" and "S" attribute types, respectively. And special care may need to be taken for dynamic properties (type "D" returns an empty string, rather than `NULL`) - I'm not totally sure how to verify they have an implementation, I suppose checking the selector is fine but I don't know how that would work with `NSManagedObject` sublcasses that use KVC for their properties, primarily. – Carl Veazey Jan 14 '13 at 22:23
  • I think for actually INVOKING such stuff at runtime, KVC is the way to go. I am just going to use this to check "signatures" at runtime, "is this an object with a name property that returns a string?" Yes? Then it's "a named-object" for my purposes. If I want to read stuff, I will rely purely on KVC. – Warren P Jan 14 '13 at 23:16