4

I'm trying to dynamically determine the type of a property in Objective-C. Based on what I have read on this site and elsewhere, I believe I am doing the right thing. However, my code isn't working.

The code snippet below demonstrates the problem. Attempting to get the property information for "backgroundColor" and "frame", both of which are valid properties of UIView, fails (class_getProperty() returns NULL):

id type = [UIView class];        
objc_property_t backgroundColorProperty = class_getProperty(type, "backgroundColor");
fprintf(stdout, "backgroundColorProperty = %d\n", (int)backgroundColorProperty); // prints 0

objc_property_t frameProperty = class_getProperty(type, "frame");
fprintf(stdout, "frameProperty = %d\n", (int)frameProperty); // prints 0

Enumerating the properties as described here doesn't produce the expected results, either. The following code:

NSLog(@"Properties for %@", type);
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(type, &outCount);
for (i = 0; i < outCount; i++) {
    objc_property_t property = properties[i];
    fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}

generates this output:

2012-03-09 13:18:39.108 IOSTest[2921:f803] Properties for UIView
caretRect T{CGRect={CGPoint=ff}{CGSize=ff}},R,N,G_caretRect
gesturesEnabled Tc,N
deliversTouchesForGesturesToSuperview Tc,N
skipsSubviewEnumeration Tc,N
viewTraversalMark Tc,N
viewDelegate T@"UIViewController",N,G_viewDelegate,S_setViewDelegate:
inAnimatedVCTransition Tc,N,GisInAnimatedVCTransition
monitorsSubtree Tc,N,G_monitorsSubtree,S_setMonitorsSubtree:
backgroundColorSystemColorName T@"NSString",&,N,G_backgroundColorSystemColorName,S_setBackgroundColorSystemColorName:
userInteractionEnabled Tc,N,GisUserInteractionEnabled
tag Ti,N,V_tag
layer T@"CALayer",R,N,V_layer

Documented properties such as "backgroundColor", "frame", and others are missing, whereas undocumented properties like "caretRect" and "gesturesEnabled" are included.

Any help would be very much appreciated. In case it is relevant, I'm seeing this behavior on the iOS simulator. I don't know if the same thing would happen on an actual device.

Thanks, Greg

Greg Brown
  • 3,168
  • 1
  • 27
  • 37

3 Answers3

6

You are getting the UIView properties, the problem is backgroundColor is not a UIView property, is a category property. Check UIView.h. I think you can't get a objc_category, but have a look at class-dump.

fbernardo
  • 10,016
  • 3
  • 33
  • 46
  • 1
    Yep, I see it - backgroundColor is defined in a category named UIView(UIViewRendering). Any idea how to get at properties that are defined in categories? – Greg Brown Mar 09 '12 at 20:13
  • Ah, I see that you said that you don't think it is possible. If that's true, then it seems like either class_getProperty() should handle these properties or a category_getProperty() function should be provided. Think it is worth filing a bug/feature request? – Greg Brown Mar 09 '12 at 20:20
  • This seems like a relevant bug report: http://llvm.org/bugs/show_bug.cgi?id=7398. I wonder if switching to gcc might resolve the issue. – Greg Brown Mar 09 '12 at 20:40
  • I see the same behavior with LLVM GCC 4.2. – Greg Brown Mar 09 '12 at 21:15
  • Too bad... And with class dump? Like described here: http://www.cocoabuilder.com/archive/cocoa/80681-objc-category.html – fbernardo Mar 09 '12 at 21:22
0

You can find category properties as methods.

@import ObjectiveC;

static void test(Class class, NSString* methodName) {

    Method method = class_getInstanceMethod(class, NSSelectorFromString(methodName));

    const char* type = method_copyReturnType(method);

    printf("%s : %s\n", methodName.UTF8String, type);

    free((void*)type);
}

Then you inspect some...

test([UILabel class], @"alpha");
test([UILabel class], @"textColor");
test([UILabel class], @"isHidden");
test([UILabel class], @"minimumScaleFactor");

After look at these defines in runtime.h

#define _C_ID       '@'
#define _C_CLASS    '#'
#define _C_SEL      ':'
#define _C_CHR      'c'
#define _C_UCHR     'C'
#define _C_SHT      's'
#define _C_USHT     'S'
#define _C_INT      'i'
#define _C_UINT     'I'
...

Don't forget to respect getters/setters notation for BOOL properties, search 'isHidden' instead 'hidden'.

m8labs
  • 3,671
  • 2
  • 30
  • 32
0

Dodging around the issue slightly, the following works:

NSMethodSignature *signature = [[UIView class] 
            instanceMethodSignatureForSelector:@selector(backgroundColor)];
NSLog(@"%s", [signature methodReturnType]);

So the runtime may somehow have lost the fact that backgroundColor is a property but you seem to start with that information anyway in the first code snippet so it just checks out the return type of the getter.

Tommy
  • 99,986
  • 12
  • 185
  • 204
  • Yes, but you can't find out the property name dynamically... That's the problem. And what you're finding is the getter method, how would it know if it's a property or a method? (getting the methods is easy...) – fbernardo Mar 10 '12 at 02:22
  • My final paragraph was to say it seemed you knew the property existed already; while I can declare an `@property` and then provide any methods I like to implement the getter and setter, I'm not sure there is a valid distinction in Objective-C. See also e.g. `frame` — it's listed as a property in the `UIView` documentation so it explicitly is one, but it also explicitly takes effect by altering the layer's `center` and `bounds`, meaning that the getter is almost certainly a method with actual calculations in it. – Tommy Mar 10 '12 at 02:27
  • Yes, but he's asking "dynamically determine"... And there should be a valid distinction since the objc runtime has the class_copyPropertyList method. – fbernardo Mar 10 '12 at 02:38
  • Apple's documentation makes a distinction between properties and _declared_ properties, which is the same as the difference between an informal and a formal protocol. `class_copyPropertyList` gets declared properties only, but just like informal protocols other properties may exist. The fact that the syntax only arrived in Obj-C 2.0 is a clue, and the `NSMethodSignature` solution answers the question set: how to dynamically determine Objective-C property type and fits with the same code given that takes `backgroundColor` as a given. You've moved 'dynamically determine' out of context. – Tommy Mar 10 '12 at 03:05
  • Maybe you're right, it's the way I read it. Maybe this is the info he's looking for. – fbernardo Mar 10 '12 at 03:14
  • The problem with this approach is that Objective-C does not maintain object type information at runtime. So the code above will return "@" for "backgroundColor" rather than UIColor, which is ideally what I'd want. – Greg Brown Mar 13 '12 at 14:52