views:

213

answers:

1

I was very puzzled by some unexpected compile errors in my code recently. I did some troubleshooting, and I came up with this bit of test code:

1 float performSelectorResult, messageResult;
2 SEL selector = @selector(smallContentCellMargin);
3
4 NSLog (@"selector %s result %f", selector,  [defaults performSelector:selector]);
5 NSLog (@"message result is %f", defaults.smallContentCellMargin);
6
7 performSelectorResult = [defaults performSelector:selector];
8 messageResult = defaults.smallContentCellMargin;

If I run the code as-is, I get a compile error on line 7: "incompatible types in assignment". However, if I comment out line 7 the code build and runs; the results in NSLog are:

2009-07-21 18:31:44.823 ICE[24347:20b] selector smallContentCellMargin result 0.000000
2009-07-21 18:31:44.830 ICE[24347:20b] message result is 7.000000

I've used very similar code to retrieve a UIFont, but never a float. Is there something I don't know, or is this a bug in Objective-C? I'm very confused.

+3  A: 

First, this you are treating a selector as a string when logging with NSLog. Don't do that - a SEL is not a string. Use the %@ format and NSStringFromSelector(aSelector).

For the issue at hand, -performSelector: is defined to work with any selector that has an object return type. Due to the ABI, you can get away with using this for int return types up to the size of a pointer (depending upon runtime implementation details), but you generally cannot get away with it for float return values, as you've noticed.

For non-object return values, you should use NSInvocation, or -valueForKey:. In the latter case, you'll get a float boxed as an NSNumber.

Jim Correia
I tried a quick test of -valueForKey: 'float quickTest = [defaults valueForKey:@"smallContentCellMargin"];' When I try to compile the code, I get an "incompatable types in initialization" error. I looked up -valueForKey in the Documentation, and it says that it returns the type (id) - did I do something wrong, or is it the same as -performSelector in this case?
JoBu1324
+1 This is a good opportunity to learn more about how messages are actually passed. We often say that messages are passed w/ objc_msgSend(), but that's a simplification. Look in the docs for objc_msgSend() and compare it to its friends, specifically objc_msgSend_fpret() in this case. Note that the OP code *would* work on PPC, but not Intel. And that's the kind of platform-dependency you don't want to be relying on. Use Jim's approach, even for ints where you can get away with it on Intel.
Rob Napier
Re-read Jim's comment about -valueForKey: returning an NSNumber. NSNumber is an object that represents a number (a double in this case).
Rob Napier
I deserve a biting remark instead of your tame reply! Thanks Rob. I missed the NSNumber bit.
JoBu1324
One last quick note: thanks for the NSStringFromSelector tip, Jim!
JoBu1324