Structure and Interpretation of Computer Programmers

I make it easier and faster for you to write high-quality software.

Tuesday, June 24, 2008

Objective-C NAQs

Never-Asked Questions :-)

In Code Complete 2 §6.5, Steve McConnell presents a list of class-related design issues that "vary significantly depending on the language". So why don’t we look at them for Objective-C? Especially as I can’t find anyone else who’s done so based on a couple of Google searches… N.B. as ever, I’m really considering Cocoa + Objective-C here, as the language itself doesn’t provide enough policy for many of the issues to have an answer (only method resolution and the existence of isa are defined by the runtime and the compiler – ignoring details like static string instances, @protocol and so on).

Behaviour of overridden constructors and destructors in an inheritance tree. Constructors are simple. If you can initialise an object, return it. If you can’t, return nil. The -[NSObject init] method is documented as simply returning self.

For destructors, we have to split the discussion into twain; half for garbage collection and half for not. In the world of the Apple GC, destruction is best done automatically, but if you need to do any explicit cleanup then your object implements -finalize. The two rules are that you don’t know whether other objects have already been finalized or not, and that you must not resurrect your object. In the world of non-GC, an object is sent -dealloc when it has been released enough times not to stick around. The object should then free any memory claimed during its lifetime, and finally call [super dealloc]. Note that this means objects which use the voodoo cohesion of “OK, I’ll clean up all my resources in -dealloc” probably aren’t going to work too well if the garbage collection switch is flipped.

Behaviour of constructors and destructors under exception-handling conditions. Exceptions are a rarity in Cocoa code (we didn’t have language-level exceptions for the first decade, and then there was a feeling that they’re too expensive; C++ programmers like the Leopard 64-bit runtime because ObjC exceptions and C++ exceptions are compatible but that’s not the case in the NeXT runtime) but obviously a constructor is an atomic operation. You can either initialise an object or you can’t; there’s no half-object. Therefore if an -init… method catches, the thing to do is unwind the initialisation and return nil. The only thing I can think to say for having exceptions around destruction time is “don’t”, but maybe a commenter will have better ideas.

Importance of default constructors. All classes implement -init, except those that don’t ;-). Usually if there’s a parameterised initialiser (-initWithFoo:) then that will be the designated initialiser, and -init will just return [self initWithFoo: someReasonableDefaultValueForFoo];. If there’s no reasonable default, then there’s no -init. And sometimes you just shouldn’t use -init, for instance NSNull or NSWorkspace.

Time at which a destructor or finalizer is called. See How does reference counting work? for a discussion of when objects get dealloced. In the garbage-collected world, an object will be finalized at some time after it is no longer reachable in the object graph. When that is exactly really depends on the implementation of the collector and shouldn’t be relied on. Temporal cohesion (“B happens after A, therefore A has been done before B”) is usually bad.

Wisdom of overriding the language’s built-in operators, including assignment and equality. This is such a brain-damaged idea that the language doesn’t even let you do it.

How memory is handled as objects are created and destroyed or as they are declared and go out of scope. How does reference counting work? How do I start using the garbage collector?

posted by Graham Lee at 22:33  

No Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment

Powered by WordPress