===== 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 [[http://www.wilshipley.com/|Wil Shipley]] of [[http://www.delicious-monster.com/|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 [[http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html#//apple_ref/occ/instm/NSObject/init|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 [[http://diveintomark.org/archives/2004/08/16/specs|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 [[http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html|The "Double-Checked Locking is Broken" Declaration]].
For languages that have bindings to [[POSIX threads]], I recommend the use of ''[[pthread_once()]]''.