All combined, there are 10 Apple operating systems you might need to support:
In between, in the OS X 10.5 and 10.6 zone, you might want to add gc and non-gc support as options.
There are six aspects to consider:
Here are the details.
Your target platforms. If you don't need to target OS X 10.4 Tiger, then everything is really simpler. iPhone SDK shares largely the same foundation of OS X 10.5 SDK with Objective-C 2.0 support (except gc) and all the new types and methods in Foundation. If you still need to target Tiger, your options are (1) using entirely the 10.4 SDK, or (2) using 10.5 SDK but targeting 10.4. Although (1) is the easier way, it's really the last option I recommend you to take. The second path requires more careful planning (more on that later) but it makes your code more forward compatible especially if your code will eventually run on 64-bit platforms.
Endianness. Turns out this is the easiest to solve especially if you don't naïvely do primitive data serialization, which you shouldn't do anyway because of the issues of modern C (e.g. that compilers do so many things for you, plus that modern computer architecture is adamant on alignment). Stick to the fact that other than PowerPC everything else (Intel, ARM on iPhone) is little endian. The old-style Apple's 32-bit constant (things like 'lpcm'
which is the value of the constant kAudioFormatLinearPCM in Audio Toolbox) is read 'cmlp'
if you dump the 32-bit word on Intel.
Width and type of the primitives. I have witnessed the 16-bit to 32-bit transition when I was in middle school, and anyone who has done their bit in Win16 would told you the folly of Hungarian notation and the historical debacles on types such as WORD
, DWORD
, and so on. This issue is now alive once again as many of us are now targeting 64-bit. Gladly the problem is well understood and people now know better. Use fixed-width primitive typedefs like uint8_t
or uint32_t
is the common practice nowadays. For standard C code, use things like size_t
and SIZE_MAX
for POSIX-land or platform-agnostic code, and they'll do fine on Windows and Linux too. As for Cocoa, since 10.5 SDK there are new typedefs which Apple now use throughout their framework code: NSInteger
, NSUInteger
, CGFloat
, and so on. NSInteger
and NSUInteger
is the widest (unsigned) integer on the platform you're targeting. (int
and long
aren't reliable types by name because of history). This is the most compelling reason you should use OS X 10.5 SDK even if you're targeting Tiger. For framework designers, however, chances are your code might be required to be built under both 10.4 and 10.5 SDK. My solution is to use typedefs in the implementation files.
Logging of the primitives. Use %ld
for NSInteger
and %lu
for NSUInteger
as recommended by Apple.
Using only the common denominators in the system library. This is unfortunately the trickier part. Again the most important things are:
-[NSObject className]
for one. Use NSStringFromClass()
instead. Take extra care of NSFileManager
methods. Many of them don't exist there either!-[NSNumber integerValue]
. Unfortunately this is the biggest problem if you are using 10.5 SDK and still targeting 10.4. You call respondsToSelector:@selector(integerValue)
and compiler gives you warning—but a category can shuts the compiler warning off. Not for performance-intensive code. Should probably consider conditional compilation.if (x)
before you call x()
if it's a 10.5-only function.autorelease
is an NOP in gc. Use NSMakeCollectable
, which is actually a CFMakeCollectable
wrapper but is nil
-safe. And yea, Apple is benevolent enough to have supplied that function in 10.4 SDK (at around OS X 10.4.8 or 10.4.9, I can't exactly recall), so you don't need to check if that's a zapped function.Conditional compilation. This is the biggest bag of hurts. I only use these variants.
To determine if if it's on OS X 10.4 SDK, targeting 10.4:
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
#endif
To determine if we're on OS X 10.5 SDK/iPhone SDK, able to use Objective-C 2.0 and all:
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4
#endif
To determine if we're on OS X 10.5 SDK (but probably targeting 10.4 too):
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
#endif
If on iPhone:
#if TARGET_OS_IPHONE
#endif
If you have AppKit (i.e. on Mac but not on iPhone), e.g. for NSModalPanelRunLoopMode
:
#if TARGET_OS_MAC && !TARGET_OS_IPHONE
#endif
If on iPhone Simulator:
#if TARGET_IPHONE_SIMULATOR
#endif
ObjectiveFlickr have actually done all the things above, and that's how it can support a great number of current Apple platforms (I can't claim all, since I haven't done a complete coverage test).