Moving Cocoa from PPCMacs to IntelMacs
Still trying to digest what Steve Jobs just announced, and the repercussions of that (Microsoft moves the XBox to PPC, and Apple drops the platform like it had the plague ... coincidence?). To while away the time, I thought I'd start investigating changes needed to my apps to make them Intel-ready.
The first thing that came to mind was endian-ness. Most parts of the app should just work fine. Property lists and other XML stuff are insulated against endian-issues, being text and UTF-8 and all that, and I'd suppose the binary variants contain a byte-order marker.
But if you're writing out your own binary data, you'll want to use the macros in Endian.h to swap your numbers from on-disk format (big-endian) to the platform's native format. Here's a table of which functions to use:
C Type | To Disk | From Disk |
---|---|---|
unsigned short | EndianU16_NtoB() | EndianU16_BtoN() |
signed short | EndianS16_NtoB() | EndianS16_BtoN() |
unsigned long | EndianU32_NtoB() | EndianU32_BtoN() |
signed long | EndianS32_NtoB() | EndianS32_BtoN() |
unsigned long long | EndianU64_NtoB() | EndianU64_BtoN() |
signed long long | EndianS64_NtoB() | EndianS64_BtoN() |
Note that if you're just starting to write your app and want your binary file format to be optimal for Intel Little-Endian numbers from the start, you may want to replace the "B"s in the above function calls with "L"s. That way, you'll make your app write out Intel-style little-endian data from the start. And of course, all these macros map to no-ops if you're trying to convert big-endian data to big-endian, or little-endian to little-endian.
Or, if you want to be compatible at less performance cost, save a byte-order marker at the start of your files (e.g. a short set to "1" -- if it's not 1, it probably needs to be swapped), and only perform swapping as necessary.
If you have flags in one of your own persistent classes, you probably used the following non-endian-safe construct to get your flags saved in an extensible way:
struct UKDVFlags { unsigned int flag1:1; unsigned int flag2:1; unsigned int flag3:1; unsigned int reservedFlags:29; };
This lumps all these bit-flags together in one unsigned int. If I save this to disk, instead of the first 3 bits, my flags will end up in the last 3 on a little-endian system like Intel. So, what do we do? Well, the party-line that NSView.h demonstrates is to change this into:
struct UKDVFlags { #ifdef __BIG_ENDIAN__ unsigned int flag1:1; unsigned int flag2:1; unsigned int flag3:1; unsigned int reservedFlags:29; #else unsigned int reservedFlags:29; unsigned int flag3:1; unsigned int flag2:1; unsigned int flag1:1; #endif };
Your old flags go in the __BIG_ENDIAN__ clause, while the little-endian version gets the flags in reverse order. That's all. You can even wrap this in a union, such as
union UKDVFlagsUnion { unsigned int allFlags; struct UKDVFlags bits; }
And you'll always get the same value in allFlags, and the same bitfield members in bits will be set, so if you save allFlags using an NSNumber, your flags will survive endian-conversion just fine.
Update: For some odd reason, I claimed allFlags would have a different number, but the same bytes. Nonsense. Changed.