Friday, February 20, 2009

Cross Development Hint

I'm back to writing some Cocoa code after doing nothing but iPhone work for almost a year (March last year until now). It's a nice change, and in a way, the similarities between the two almost make it harder for my brain to switch gears. I keep trying to use UIxxxxxx instead of NSxxxxxx.

Anyway, there may be times when you want to share code between a Mac and an iPhone program, and for the most part, that's not going to be a problem. However, there are a few gotchas.

First and foremost, if you're using Garbage Collection on the Mac (and you really should be at this point, and at worst you should make the switch when you move to Snow Leopard), how do you define your properties so that they work in both environments? Here's one way. Somewhere in a header file that all of your files will #import, add this:

#define ASSIGN_OR_RETAIN retain
#define ASSIGN_OR_RETAIN assign

Now, when you go to declare your properties, do it like so:

@property (nonatomic, ASSIGN_OR_RETAIN) NSString *name;

Your properties will be defined properly in both environments. Make sure you've enabled GC on the Mac target, however, or you'll have problems. GC is still opt-in at this point. If you're not going to use GC, then there's no need to do this.


schwa said...

Two points:

"and you really should be [using Garbage Collection] at this point, and at worst you should make the switch when you move to Snow Leopard"

Most of my "smart friends" still doing Mac OS X development have dabbled with Garbage Collection and have been bitten by it at some point. The conclusion I kept hearing was "GC isn't ready for primetime yet". Now of course that was a while ago and things may have changed since then. And with the other technical issues with GC (linking GC and non-GC code) it isn't cut and dried. But still. Blanket statements like "you should be using GC" are really up for debate.

Point 2. The #define trick just isn't at all necessary. From " the methods that are used to implement the manual reference counting system (retain, release, dealloc, autorelease, and retainCount) have no effect—the Objective-C messenger short circuits their dispatch".

Just use retain and move on with your life. It should be understood that using retain in an @property declaration is just like using retain/release in code. It becomes a no-op in GC-worlf.

Of course if you're writing code that lives in GC and non-GC worlds then you still need to be aware of traps like retain cycles and so on. You need to mark delegates and parent pointers as assign-type properties and so on. But your #define approach doesn't address that issue either.

Jeff LaMarche said...

Yeah, I've been bitten by Garbage Collection in the past. It made me very gunshy of it. Plus, I have quite a comfort-level with retain-counting, so I was pretty skeptical of GC until fairly recently myself.

IMHO, the situation has changed. The now-open source Obj-C garbage collector is solid and works well. I haven't used it too much myself, to be perfectly honest (I was just starting to be convinced of it when I made the jump to iPhone stuff full-time) but everything I've done with it recently has worked perfectly, so it appears to me that the problems have been mostly worked out and the GC is ready for prime time. I've also been encouraged to use GC by several people whose opinions I trust, including several engineers from inside Apple. Simply put, GC is now faster than non-GC (something I didn't believe, but I've seen the benchmarks) and the disparity will grow exponentially with OpenCL and Snow Leopard since Garbage Collection can happen outside of the main thread, using available cores or processors.

Perhaps I should have phrased that differently - instead of "you should be using GC", perhaps "Apple is encouraging developers to move towards GC" would have been better.

Of course, very few things are black-and-white, but from the things I saw at the "What's coming..." sessions at WWDC last year, I remain firmly of the opinion that Cocoa / Mac developers should be moving toward GC quickly, if not using it now. Of course, there are considerations like backwards compatibility with hardware and with using existing non-GC code, so my blanket statement was probably not the wisest choice of words.

As for your second comment, it is true that specifying retain is a no-op in a GC environment so you can get away without doing this. However the docs say you should use assign when using GC, optionally specifying __weak or __strong refs. I haven't used the optional terms in the example here, but by defining different values for the two environments, I have the flexibility to use the strong or weak hints without messing up the iPhone compile, simply by changing the #define or turning it into a macro.

ZuCom said...

NIce ....