All About Casting
A few days ago Christopher & I chatted with an iPhone in Action reader about why Listing 18.3 in the book generated the warning "Warning: 'UIView' may not respond to -addPic:at:'". The answer is casting, and I think it's a topic that deserves some additional discussion.
All About Casting
Casting is a fundamental of C. Wikipedia defines it as an "explicit type conversion." We demonstrate it briefly on p.156, where we show that you can turn a float into an int with a little bit of casting magic:
float a = 6.00;
int b;
b = (int) a;
Beyond that, we give it pretty short attention. That's probably great for folks with experience with rigorous programming languages, but less useful for those of you just getting into them for the first time.
So in this article I'm going to talk more about casting by concentrating on three three times that I've seen it most in SDK development (and thus where you're most likely to encounter it). In order I'm going to cover: toll-free bridging; math casts; and, finally, method casts (the topic that got us started).
Toll-Free Bridging
This is the other cast-related topic that we covered pretty extensively in iPhone in Action. There are some details on p.157 (at the end of the discussion of casting) and on p.321 (in the "Using Core Fondation" box).
Much of your iPhone programming will be done using Cocoa frameworks, such as the UI and NS libraries. However, sometimes you'll have to instead use Apple's older libraries, such as Core Foundation. Core Foundation is object-oriented, just like the NS libraries, and as a result you end up with alternative ways to represent a lot of standard variable types. For example Core Foundation's CFStringRef and Cocoa's NSString * are alternate ways to represent strings.
Fortunately, Apple recognizes this equivalence and "toll-free bridges" equivalent Core Foundation and NS classes. In order to use one in the place of another, you just need to cast it, as we do throughout Listing 16.11 (pp.318-320):
(NSString *)ABRecordCopyCompositeName(thisPerson);
This sort of casting will come up frequently if you use older frameworks, such as Address Book and Core Graphics.
Math Casting
I've run into the most problem with casting when I was writing mathematical formulas. Listing 17.10 (pp.340-341) shows off one such examples. Most of the way through that example, you can see a line which reads:
int currentHeight = 395 - ceil((float)newLocation.altitude/savedDestinationHeight*(401-65));
Clearly, that's a cast, but I don't really explain why in the description of the code.
I had to cast because of the "int" variable that I'm saving the formula off to. A problem arises because there's not necessarily a consensus (among all programming languages) for how to treat the right-hand side of such an equation. Should the elements all be treated as integers, or should they be left in their native form until the final conversion is done? Apple's Objective-C appears to assume a conversion to the final variable type almost immediately.
In this situation that resulted in altitude/height being made into an integer before it was multiplied by 401-65, which was not the desired result, because I was trying to generate a percent (meaning that 0 or 1 was really not useful). Thus the cast of that division as a float.
I find this sort of thing to be the one situation where a cast is really required ... where leaving it out results in your program not working, and it can be very confusing to figure out why.
Method Casting
And so finally we come to query that started this article off. If you compile the code associated with Listing 18.3, you'll see "Warning: 'UIView' may not respond to -addPic:at:'". This is a non-fatal warning that has to do with yet another casting nuisance. You'll find the line that generates the warning at the top of p.353:
[self.view addPic:myImageView.image at:myImageView.frame];
If you think about it, the reason for this warning should jump out. The view controller's view property is defined as a UIView, which does not contain the addPic:at: method introduced in CollageView. Thus, if you don't like the warning, you can just cast the property appropriately:
[(collageView *)self.view addPic:myImageView.image at:myImageView.frame];
You can run into this sort of thing when a property or method assumes a very specific class of object, rather than the more general "id." If memory serves I also hit it when using the nextResponder method (probably in Listing 14.2 on p.247).
Some Final Words on Casting Warnings
It's important to note that casting problems will generally result in warnings.You don't have to resolve them. In particular a warning about uncasted toll-free bridging or a warning about an uncasted method isn't a problem as long as you know what you're doing. It's usually worth fixing them, just to make sure that you understand the reason for the warning, but you don't have to if you're doing something quick-and-dirty or have some reason to leave the warning in.
(In the case of the warning generated by Listing 18.3 we opted to leave out the casting to keep the code more readable, not really thinking about the fact that people might be concerned about the warning if they compiled the code.)
Much more insidious are the math-related casting problems which probably won't generate a warning at all. In my opinion, they're what you really have to watch out for--and they're also a good reason to generally be conscientious about casting all the time, so that you don't run into mystery problems without warnings when you get careless about math casts.
No comments:
Post a Comment