I just downloaded the Eclipse SDK to check out a plugin for it, and I noticed it was done in Carbon. That got me thinking about the state of Carbon these days. Last year at WWDC, it was made very clear that Carbon is in a holding pattern now. Maintenance mode.
I can understand why Apple might do this, Cocoa is a full-featured application framework, and the objective-C runtime does allow you do do some pretty interesting things with it. Why have two frameworks when you can focus on one. Also, now that Carbon did its job and got the developers over to X, it can be put out to pasture.
Sadly, I think this is a bad move overall. And not because I put so much hard work into it, but simply because I think it allows you to do some things easier than Cocoa. Arguably more important things.
Atomicity and Layering
Frameworks allow you to get all types of behavior for free. They are great, except when you can’t use them, say if you already have a framework for your app that’s cross-platform. In the past, this inability to use the native framework has meant reinventing the wheel in order to simulate what the OS or framework does. When the OS changes behaviors or add features from release to release, the people using the framework get it all for free. While the people who rolled their own are left behind looking out of place or just ‘off’ somehow.
It doesn’t have to be this way. A framework should be based on simple, lower-level APIs. More complex patterns are based on those APIs to create higher-level APIs. APIs that encapsulate a lot of automatic behavior should be built on the lower APIs so that if the high-level APIs can’t be used by a legacy application, it can use the lower-level APIs to at least get most or all of the right behaviors.
When we did Carbon Events, we introduced the idea of ‘standard handlers’ which would do the correct default thing. But the best part about it was that you didn’t need to adopt the entire Carbon Event model â€” just the part you needed. Whenever possible, we allowed applications to install those event handlers themselves, so even if they weren’t fully Carbon Event savvy, they’d get the right effect. We didn’t do this 100%, and there were a couple of omissions (which I think they might be fixing in Leopard based on last year’s WWDC talks), but it was the general plan: allow apps to use the same lower-level utility functions the toolbox itself used. Over time, perhaps an application could convert completely over.
This approach allows any level of adoption and can allow a gradual adoption of the framework. Sadly, it is not the Cocoa approach. It’s pretty much all or nothing on that side of the fence. And attempts to break out of the sandbox aren’t fun. For example, we want to draw things in our own contexts for Konfabulator, I can’t tell you the amount of pain we had to go through in order to try to make that happen. In the end, we still can’t get text views to draw where we want and with the transforms we want. I want to be able to tell the NSTextView draw in this context, right now (and not to draw at any other time). But I can’t without much pain and mess. This is why we cannot rotate text views in Konfabulator on the Mac. Our Windows version however, can. Why? Because you can do things more piecemeal on that platform.
I should also point out that we’ve pretty much bypassed the entire draw loop in Cocoa at this point because of those rotation issues. The AppKit seems to not like rotation at all. Well, it does it, but because it assumes control of everything, you can’t really pull it off they way we want to because it seems to call setNeedsDisplay on rotated items while it’s drawing. Huh? That should be illegal. I have a sneaking suspicion they did it as a quick bug fix to ensure the right part of the screen is drawn. But I digress…
Carbon, on the other hand, can be used as a framework, using APIs like RunApplicationEventLoop() to drive your app, or it can be used piecemeal. This dual nature allows applications to use the pieces they want and avoid the pieces they don’t (or can’t). So I could use a Carbon Window and easily put in a custom view for the backdrop (see my previous post with a sample app that does this). The view system is also more componentized, so it’s definitely more possible to do the types of things we want to as mentioned above. Heck if it did rotation, we’d probably just use it as-is. And yes, you might say I’m biased towards HIView since I wrote most of it, but it simply plays with others much better than Cocoa. And that is the whole point.
Apple offers Objective-C++. You can compile Objective-C and C++ in the same module. But the two languages do not work together. This forces people to break their C++ encapsulation and expose what should be private methods and members to Obj-C classes they are wrapping or otherwise working with. Ideally you should be able to ‘friend’ an Obj-C class.
You should also be able to have an object with a constructor be part of an Obj-C class. Without this, it violates the use of idioms like smart pointers, etc. used by most every C++ framework out there.
These are just two examples, but to me they are a huge oversight. I can’t remember if they are at all addressed in Objective-C 2.0, but I’m thinking they probably aren’t. I think that’s more geared towards garbage collection. Objective-C was, for its time, quite forward-thinking. However, these days, it’s way behind the curve. Microsoft’s Common Language Runtime (CLR) is significantly more advanced. It already allows garbage collection and dynamic behavior, while still allowing template-like solutions (called Generics), etc. It’s very well done.
The other general language issue is that I have to use Obj-C to code for Mac these days. There’s no choice about it. If you want to use some of the newer features of the OS, you must use the Obj-C API. There is no C API available! Contrast this to the Microsoft approach where a developer can code in whatever language they want on top of the CLR. In my mind, that’s the right idea. I brought this issue up a couple of times both when I was at Apple, and after.
In The End…
I keep wondering if we should switch Konfabulator over to Carbon because Cocoa is too much of a bear to work around. We have our own cross-platform framework, and Cocoa gets in our way more than it helps us. The only benefits we are getting from Cocoa right now are more image formats (animated GIFs and TIFF), as well as better font handling (though I could probably figure the right thing to do via ATS). Their text layout APIs are also pretty decent. If we went to Carbon, I’d probably miss those most.
I can pretty much guarantee that, where we are matching feature for feature, it would be easier to do what we want with Carbon (and it would take less code) due to our being implemented in C++. Cocoa simply does not lend itself to the atomic/layered approach. APIs are starting to appear here and there to let you work outside of the box, but it’s not enough. Our Cocoa codebase is a mess, mostly because of the way it interfaces (or really, doesn’t) with the rest of our code.
So I really would like to see Carbon continue because it actually works well integrating it into already-existing code.
I think the most tractable solution is to pull Cocoa apart so to speak and rebuild it to allow a more componentized use model. Simply put, take the good things about Carbon and rearchitect Cocoa to follow suit. This would probably make adoption a lot easier, and more of something that can be done over time. Basically, I think the Carbon approach is better, but Cocoa has all the latest gizmos they add each OS release. If they could just marry the two and fix the holes in Obj-C++ it’d be a huge step forward.
But I’d still rather code in C++/C#
Update: I should point out that these days (really, ever since starting coding for iPhone) I’ve been assimilated and I’m now a complete Obj-C convert. Oh how times change!