Myths

This page is where I list some common software myths and anti-patterns that I’ve encountered.

Assuming the Return Value of [super init]

Some people have decided that assigning self to the return value of [super init] in a subclass initialization method is redundant or incorrect. Here is the pattern you will find in popular books about Objective-C:

- init
{
    // Perform superclass initialization:
    if ((self = [super init]))
    {
        // Perform instance initialization here...
    }
 
    return self;
}

In some examples, you’ll see the first line of the method split into two lines, one for the assignment, and another for the comparison. This is because using an assignment as a conditional can be confusing to new programmers, and it’s also possible that some people simply feel that it is bad style.

Here is the alternate pattern suggested by Wil Shipley of Delicious Monster:

- (id)init;
{
 if (![super init])
   return nil;
 
 [...initialize my stuff...]
 return self;
}

There are two differences to note here. The first is that he is only testing for a nil return. This is based on the assertion that there are only two valid return values for [super init]: nil, and the object reference returned by the call to -alloc (in other words, the previous value of self). In this case, the assignment would be redundant.

And, as it turns out, this is almost always true. In Cocoa, there are only a handful of classes like NSDocumentController which return different values from -init than -alloc the second time they are called. This is because they are singletons, and it would be a programming error to try to allocate two instances of these classes. Unfortunately, Cocoa frameworks are not the only place you’ll find objects you’d like to subclass.

Here is what the official Apple documentation says on the subject:

Subclass implementations of this method should initialize and return the new object. If it can’t be initialized, they should release the object and return nil. In some cases, an init method might release the new object and return a substitute. Programs should therefore always use the object returned by init, and not necessarily the one returned by alloc or allocWithZone:, in subsequent code.

This means that in order to maintain the semantics of -init, it’s necessary to capture its exact return value in self before performing any other initialization, regardless of whether you know that none of the classes you are subclassing from aren’t going to return a substitute instance. Doing otherwise makes an assumption that violates the contract of the API specification, and qualifies as a case of programming by coincidence. I realize that this statement causes me to fall into the asshole category of programmers.

In fact, I have seen several cases in the wild of a “cached collection” class returning previously instantiated objects based on their initilization parameters. This is similar to what could be called OO memoized lookups.

Regardless, if [super init] were to return a different object from the one returned by the preceeding alloc, then self would point to the new object, and all subsequent instance variable assignments or messages would go to the new object, right? This makes the assumption that statements of the form ivar = newValue in Objective-C are literally syntax sugar for self->ivar = newValue. Let’s see if this assumption holds up:

Here’s an example that shows modifying the self reference and then writing to an instance variable:

@interface Test
{
    int myInstanceVar;
}
 
@end
 
@implementation Test
 
- init
{
    myInstanceVar = 1;
 
    self = (id)0xff00ff00;
 
    myInstanceVar = 2;
}
 
@end

If we compile this code on an Intel Mac, here is the result:

% cc -c Junk.m
% otool -tV Junk.o
Junk.o:
(__TEXT,__text) section
-[Test init]:
00000000        pushl   %ebp
00000001        movl    %esp,%ebp
00000003        subl    $0x08,%esp
00000006        movl    0x08(%ebp),%eax
00000009        movl    $0x00000001,(%eax)
0000000f        movl    $0xff00ff00,0x08(%ebp)
00000016        movl    0x08(%ebp),%eax
00000019        movl    $0x00000002,(%eax)
0000001f        leave
00000020        ret

As you can see, the first time after we point self on the stack at a fake instance with an easy to recognize memory address, all instance variable access afterward will use this new value as an indirect.

The second thing is that he favors moving the initialization code outside of the conditional block and returning early for style reasons. I agree with this in general, except in the case when multiple statements of repeated cleanup code are necessary before returning from a method (this usually warrants the use of another method). In addition, it is usually better to put the most common case inside of a conditional block, so that the processor doesn’t need to branch over a bunch of code that won’t be executed the majority of the time.

Double-checked Locking Idiom

The following code was widely considered to be an optimization of the double-locking pattern for thread-safe lazy-initialization of an instance variable. Shown below in a Java singleton example:

public class Singleton
{
    private static Singleton instance = null;
 
    private Singleton()
    {
        // Instance initialization here...
    }
 
    public static Singleton getInstance()
    {
        if (instance == null)
        {
            synchronized(Singleton.class)
            {
                if (instance == null)
                    instance = new Singleton();
            }
        }
 
        return instance;
    }
}

While clever, this pattern is naïve. At best, it is compiler and platform-dependent, and at worst it is simply wrong. In short, it is incorrect because of various compiler optimizations such as write reordering and constructor inlining. See The "Double-Checked Locking is Broken" Declaration.

For languages that have bindings to POSIX threads, I recommend the use of pthread_once().

 
software/myths.txt · Last modified: 2008/01/05 18:03 by sethk
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki