In 2002, Apple quietly introduced the Objective-C++ compiler. Almost nobody noticed. This is a shame, because adding a little bit of C++ to your Objective-C programming can make your code shorter, clearer, more type-safe, faster, and easier to read and write. It is nothing short of revolutionary.
In this series of blog posts, we will explore the reasons why ObjC++ is so elegant and powerful, and how best to apply it to your Cocoa programming. It's a big topic, so we'll take it in pieces. This first post will, I'm afraid, be a bit of a teaser — I just want to give you the big picture, and then you can check out subsequent posts for the details.
Is your Cocoa a little bitter?
We all love Cocoa for what it can do for us. We produce beautiful Mac and iOS apps, and the Cocoa framework does most of the heavy lifting for us. However, the Objective-C language itself is, let's admit it, not always the sweetest thing to read and write. Simple things like making a date object for a given month, day, and year ends up requiring code like this:
CFGregorianDate gd = {0}; gd.year = year; gd.month = month; gd.day = day; timeZone = CFTimeZoneCopySystem(); CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(gd, timeZone); NSDate *d = (NSDate*)CFDateCreate(NULL, absTime);
But with the "Date" ObjC++ class, you instead just do:
Date d(year, month, day);
The astute reader will point out that you could just as easily create a standard ObjC function to initialize an NSDate, even using a prototype to make it look as though Apple hadn't overlooked this basic need. That's certainly true, but there are other things you can do with an ObjC++ class that you can't do in straight ObjC at all. Chief among these is operator overloading.
Now, I'll be the first to admit that operator overloading can be abused, and end up making code less clear rather than more. But for many conceptually simple data types, the intended meaning of standard operators is quite clear, and using them greatly improves the code. Or so it seems to us, anyway; do the taste test for yourself. Cocoa A:
NSString *a = @"app"; NSString *b = [a stringByAppendingString:@"le"]; assert([b isEqualToString:@"apple"]); assert([b compare:@"banana"] == NSOrderedAscending); b = [b stringByAppendingString:@" pie"]; assert(![b isEqualToString:@"cherry pie"]);
...and Cocoa B:
String a = "app"; String b = a + "le"; assert(b == "apple"); assert(b < "banana"); b += " pie"; assert(b != "cherry pie");
Same number of lines in this case, but which is sweeter? We strongly favor the latter; it tastes very similar to other fresh, modern languages, rather than something that's been sitting in the back of the pantry since the 1980s. And despite its uber-coolness, these String objects are still fully interchangeable with any NSString* or CFStringRef your other code requires.
Memory Management
Standard ObjC, especially on the iPhone which lacks garbage collection, uses a manual reference-counting scheme. It's not all that difficult once you get the hang of it, but it often does require a bit of thought. There are two problems with that: first, having to stop and think about the memory management interrupts your work flow and distracts you from what you ought to be thinking about, which is whatever problem you're writing code to solve. Second, if you're distracted or confused or the junior programmer on the team, you will sometimes screw it up, and then you have either a leak or a crash. These bugs are hard to track down because the symptoms only appear later, while the real bug occurred a long time ago in a routine far, far away.
Enter ObjC++. A simple wrapper for any Cocoa class can handle the grunt work of retaining and releasing the reference for you. As with the String example above, you just create objects on the stack, use 'em, and forget 'em. They go poof automatically when they go out of scope, and when there are no more references to the underlying Cocoa object, it goes away too. It all Just Works™.
Type Safety
ObjC is a quasi-typed language. Base types like int, char, and double are strongly typed (though with implicit numeric conversions). But there are darn few of those; most of the "real" types you use are classes, and these are much more weakly typed. This is a powerful feature touted by Cocoa proponents, but it's also a source of bugs. We see this especially in container classes like NSArray and NSDictionary; any container can contain pretty much anything. You have to either check the type of each object as you pull it out, or trust that the other code that loads the container (you know, the stuff written by Bill three months ago and likely to be modified by Alice six months after you've moved on to another project) is only putting in types you expect.
That's pretty much necessary because Objective C lacks generics. But Objective C++ doesn't. Like operator overloading, I do think templates can be taken too far; but used judiciously, they're fabulous for making your intentions clear, and empowering the compiler to catch mistakes early.
At the same time we gain type safety, we can get some really convenient syntax too, like the [] indexing operator, or automatic boxing and unboxing of basic types. All this means we can write stuff like this:
Array b; b.append(1); b.append(2); assert(b.count() == 2); assert(b.lastItem() == 2); b.setValueAtIndex(42, 1); assert(b[1] == 42); assert(b.pop() == 42); assert(b.lastItem() == 1);
...and if we try to b.append(@"foo"), the compiler will put a stop to that nonsense right away. And again, the sweet Array object is fully interchangeable with standard NSArrays. (The standard ObjC version of this code is left as an exercise for the masochistic reader.)
Sweet, sweet Cocoa
Cocoa, as a framework, is healthy and filling. It can do most of the work of your application for you, make sure your app has the standard look & feel, and help future-proof your app against updates to the OS. But the Objective-C language that's goes along with it can be a little bitter, to say the least. As C-derived languages go, it's pretty lacking in modern features (though to be fair, as Smalltalk-derived languages go, it's not too bad).
When Apple gave us Objective-C++ in 2002, they provided an amazing new sweetener (that won't make your app fat). Now we have such ingredients as operator overloading, default parameters, function overloading, and objects that can live on the stack as well as the heap. Combined in just the right ways, these ingredients make for a Cocoa that is far sweeter than anything you've tried before. Once you try it, you'll never want plain Cocoa again.