In this second part of our series on the upcoming edition of Oxygene for Cocoa, codenamed “Nougat”, i want to go into some more details about the compiler — the heart and soul of the product — and what we plan for it to deliver.
As mentioned in Part 1, Nougat is a compiler for the Objective-C runtime, and will generate code on the same level as Apple’s Objective-C compiler (Clang/LLVM) does. As such it will generate (Delphi lovers, rejoice ;) CPU-native executables —a first for Oxygene — for the following three targets:
- 64-bit Intel Mac OS X Applications
- 32-bit ARMv7 iOS Applications
- 32-bit Intel iOS Simulator Applications
Your first question might be “why only 64-bit for OS X”, and the answer is twofold:
One: No-one really, seriously, still creates 32-bit Mac applications. Every Mac sold has been capable of running 64-bit for way over 5 years now.
Two: Much more importantly, with OS X Leopard (that’s 10.5, which shipped, again, five years ago), Apple introduced what is commonly referred to as the “modern” Objective-C runtime. This is a newer, better version of the runtime that Cocoa apps run on, and many advanced features, such as ARC, are only available on that new runtime. This “modern” Objective-C runtime, you guessed it, supports the above three platforms, but it does not support 32-bit Mac OS X.
We could go out of our way to support the older runtime, but the current thinking is Nahh, it won’t be worth the hassle. If nothing else, the lack of ARC would be a major bummer.
Automatic Reference Counting
As you might have guessed from the above, Oxygene supports and fully embraces ARC, the Automatic Reference Counting model Apple introduced at last year’s WWDC. In fact, we’re so sold on ARC that we’re planning to make it non-optional.
For those unfamiliar with the matter, what does ARC mean? ARC means that the compiler automatically keeps track of reference counting for you. Under the hood, Objective-C uses a retain/release model for keeping track of object lifetimes (similar to COM and Delphi Interfaces). When wanting to hold on to an object, it used to be you’d call “retain” on it. Once you were done, you’d call “release” (and there’s the concept of autorelease pools which sweetens this memory management model some more). With ARC, you don’t have to (in fact, you cannot) keep track of retain/release — the compiler handles all of that for you.
The end result is that memory management is pretty much taken care of for you, and the kind of code you write in Nougat will be comparable to what you do in the Garbage Collected .NET and Java environments (although ARC is not garbage collection, but deterministic memory management).
Like most platforms, Objective-C (due to its roots in C) has both simple types — such as integers, booleans, C-style zero-terminated strings — and class-based types that can represent the same kind of data.
Staying in line with its counterparts for .NET and Java, where such a thing is common and expected, “Nougat” will automatically handle boxing and unboxing for numbers, string and arrays.
For example, you can simply write
5, and the compiler will take care of converting that
int to and from an
NSNumber for you, when boxing is needed. For example, you can write
var x := 5; // "x" will be an Integer. myDictionary.setValue(x) forKey('five');
var y := NSNumber.numberWithInt(5); // silly, i know. z := y+5; // "y" will be unboxed.
In a similar fashion, string literals and C-style strings and NSString will be interchangeable, with the compiler doing the right thing for you.
As a native Cocoa compiler, Nougat will need to let you reference other frameworks and libraries — be it the core operating system frameworks provided by Apple (such as Cocoa, UIKit, MapKit, you name it) or third party (or even your own) frameworks and libraries.
As mentioned in Part 1, Nougat is no bridge, and requires no mappings to be defined. Rather, Nougat directly understands and consumes the exiting Objective-C frameworks — just like you would expect, and just like Oxygene does on its other two platforms.
Now, Objective-C frameworks and libraries don’t have the rich meta data that Oxygene is used to — they are just binaries with compiled code, and C-style header files. Because parsing C (and Objective-C) headers is a time-consuming and inefficient process, Nougat introduces an intermediary format, which we call “.fx Files” that the complier references. .fx files are generated automatically by a tool that comes as part of Nougat, and you can think of them as something like pre-compiled headers — a binary representation of all the definitions found in a framework’s headers.
Nougat will come with pre-created .fx files for the major OS versions we’re supporting (right now the internal alpha drops provide OS X 10.6, 10.7 and 10.8, as well as iOS 5.1 and 6.0), so the compiler (and the IDE) is ready for you to use those frameworks right from the Go. But if there’s a new OS (or beta) coming out that you want to use, it will be as easy as a single click to get that new SDK exposed to Nougat — no need to wait for the NDA to expire and/or us to ship updated support.
How does this look/work from the developer’s point of view? Simple. Just as always, you’ll go to Add Reference to add a new reference to your Oxygene project. The IDE will show you a re-filled list of frameworks in the SDK your current project is targeting (say, OS X 10.8), and all you have to do is, say, pick “CoreLocation” from the list, and you are ready to use the APIs exposed there.
Objective-C itself does not use or know namespaces. For Nougat, types and other external definitions will automatically be partitioned into namespaces based on the frameworks they come from. So, for example, your code files might have
uses UIKit, CoreGraphics; in their uses clause, and your code will automatically have all identifiers from those frameworks in scope. If you want to use a class that’s not in scope, you can always prefix it with the namespace, just as you’d expect, i.e.