Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Subclassing and Casting in Objective C

I came across a strange problem today. I created a subclass of UIView and added only 1 method to the template code provided by xcode.

@interface FloatView : UIView {

}
- (void)floatTest:(CGFloat)x;
@end

- (void)floatTest:(CGFloat)x {
  NSLog(@"float was %f", x);
}

Then in my appDelegate I had code like this:

UIView *floatView = [[FloatView alloc] init];
[floatView floatTest:10.0f];

Pretty simple, right? What should this print out? I thought it would something like "10.0000", but no, it prints out "0.000000".

I wrestled with this for hours, trying to figure out what I was doing wrong, and then I changed the code in my appDelegate to

FloatView *floatView = [[FloatView alloc] init];
[floatView floatTest:10.0f];

Only then, did it print out the expected "10.0000". Why is this so? I've declared FloatView as a subclass of UIView, shouldn't I be able to assign a FloatView object to a UIView pointer without problems?

Even though floatView was declared a pointer to a UIView, it's really a floatView and it should be able to handle the floatTest message? Am I totally off base here?

like image 814
Kaom Te Avatar asked Dec 23 '22 09:12

Kaom Te


1 Answers

Actually, polymorphism is working as expected. If it didn't work, nothing would have been printed (in your example, 0.0000 is being printed). The thing is, while your instance actually responds to testFloat:10.0f message, since the compiler can't statically see the method declaration (as UIView class doesn't declare such a method), it assumes that your method takes ... as argument and returns id.

When CGFloat is passed to a method that expects variable number of arguments (...), it's promoted to double. Thus, the receiving method is passed a double argument and thinks it's a float and it doesn't get printed correctly.

You can verify this behavior by changing NSLog line to:

NSLog(@"%f", *(double*)&x);

When the compiler sends the message to FloatView* rather than a UIView*, it can find the exact signature of the method. It can see it really expects CGFloat and doesn't promote the argument to double. As a result, it works correctly.

Additionally, if UIView* contained the method declaration that took a CGFloat, the compiler would call the method appropriately. To summarize, this is not a polymorphism issue; it's a missing method signature issue.

like image 176
mmx Avatar answered Jan 07 '23 13:01

mmx