Archive

Archive for the ‘Tips’ Category

Nerdy Mac Tip

September 19th, 2011

Here’s a nerdy tip for Mac users. Open Automator, and create a new Service workflow. Set it to receive no input, then drag a “Run Shell Script” item into the workflow. Replace the default “cat” in the text area with this:

echo -n `uuidgen | tr A-Z a-z` | pbcopy

Save the service as “Copy New UUID to Pasteboard”. Now whenever you run the service, it generates a fresh UUID that is on your pasteboard, which you can then copy into whatever context you need it.

If you never need a UUID, then, umm, maybe just don’t worry about this tip.

Tips

Designated Initializers in Objective-C

September 21st, 2010

So, if you are a Mac/Objective-C developer, you are probably familiar with the Designated Initializer. The Designated Initializer (“DI”) is, in an Objective-C class, the one initializer in a class that is supposed to do the heavy lifting in initializing an object for use. Other initializers of the class should generally invoke the DI.

Similarly, subclasses of a given class should, in their DI, invoke the superclass DI (and the other initializers of the subclass should invoke the subclass DI). This way, any way the object gets initialized, the DI of both the superclass and the subclass are sure to be invoked.

The documentation does a decent job of explaining this, but I think it’s easy to miss an important detail, and that is that in a subclass, you must take care not only to invoke the superclass’ DI, but you also have to override it. (If your DI is different from the superclass’ DI, this is not necessarily obvious.)

For example, take these two classes:

@interface DJMySuperclass : NSObject
{
    DJMyInfo* importantData_;
    BOOL someImportantSwitch_;
}

// The designated initializer for DJMySuperclass:
- (id) initWithImportantData:(DJMyInfo*)importantData;

@end

@interface DJMySubclass : DJMySuperclass
{
    DJMoreInfo* moreImportantData_;
}

// The designated initializer for DJMySubclass:
- (id) initWithImportantData:(DJMyInfo*)importantData
           moreImportantData:(DJMoreInfo*)moreImportantData;

@end

Fairly straightforward so far, right? Now, take this implementation:

@implementation DJMySuperclass

- (id) init
{
    return [self initWithImportantData:nil];
}

- (id) initWithImportantData:(DJMyInfo*)importantData
{
    if (self = [super init]) {
        if (importantData) {
            importantData_ = [importantData retain];
        } else {
            importantData_ = [[DJMyImportantData alloc] init];
        }
        someImportantSwitch_ = YES;
    }
    return self;
}

- (void) dealloc
{
    [importantData_ release];
    [super dealloc];
}

@end

So far so good; completely obvious. Now let’s take this subclass implementation:

@implementation DJMySubclass

- (id) init
{
    return [self initWithImportantData:nil moreImportantData:nil];
}

- (id) initWithImportantData:(DJMyInfo*)importantData
           moreImportantData:(DJMoreInfo*)moreImportantData
{
    if (self = [super initWithImportantData:importantData]) {
        someImportantSwitch_ = NO;
        if (moreImportantData) {
            moreImportantData_ = [moreImportantData retain];
        } else {
            moreImportantData_ = [[DJMoreInfo alloc] init];
        }
    }
    return self;
}

- (void) dealloc
{
    [moreImportantData_ release];
    [super dealloc];
}

@end

Again, totally straightforward, right? So where is the bug?

Imagine this extension (or, maybe instead of a category extension, imagine a similar future addition to the superclass and subclass, a year down the road and done by a different developer).

@interface DJMySuperclass(JBJoeBlowsAdditions)

- (id) JB_returnsACopy;

@end

@implementation DJMySuperclass(JBJoeBlowAdditions)

- (id) JB_returnsACopy
{
    return [[[[self class] alloc] initWithImportantData:importantData_] autorelease];
}

@end

@implementation DJMySubclass(JBJoeBlowAdditions)

- (id) JB_returnsACopy
{
    DJMySubclass* other = [super JB_returnsACopy];
    [other->moreImportantData_ release];
    other->moreImportantData_ = [moreImportantData_ retain];
    return other;
}

@end

Where’s the bug?

The bug is that if you have a DJMySubclass object called foo, and you call DJMySubclass* bar = [foo JB_returnsACopy];, you’ll discover that bar->someImportantSwitch_ contains YES, when you thought you had a class invariant for DJMySubclass where someImportantSwitch_ was always forced to NO.

Why?

Because JB_returnsACopy in the superclass uses the initWithImportantData: initializer, and you didn’t cover it in DJMySubclass, so your designated initializer in the subclass never got invoked.

The fix is simple: cover the superclass’ DI in your subclass, and invoke your DI.

@implementation DJMySubclass

- (id) initWithImportantData:(DJMyInfo*)importantData
{
    return [self initWithImportantData:importantData
                     moreImportantData:nil];
}

@end

The documentation for Designated Initializers says:

It’s important to know the designated initializer when defining a subclass. For example, suppose we define class C, a subclass of B, and implement an initWithName:fromFile: method. In addition to this method, we have to make sure that the inherited init and initWithName: methods also work for instances of C. This can be done just by covering B’s initWithName: with a version that invokes initWithName:fromFile:.

General Principle: The designated initializer in a class must, through a message to super, invoke the designated initializer in a superclass.

This general principle, while true, is not sufficient. You really need to make sure to cover the superclass’ DI in your subclass, as described in that first paragraph, and shown in the accompanying diagram 3-3.

Tips ,

Comprehensive Flowchart

June 22nd, 2009

I’ve come up with a comprehensive flowchart for children to follow, outlining proper behavior in all cases. It can be used in all situations and I’ve think I’ve covered all the bases, enough to handle any scenario that they should be expected to encounter. Unfortunately I think it may be too complex for most children under the age of 8 to navigate successfully. See what you think. I’ve shown it below.

Flowchart

(Reposted from my internal blog at work.)

Tips

Get Adobe Flash playerPlugin by wpburn.com wordpress themes