Archive

Posts Tagged ‘Mac’

Usability in Hardware

November 9th, 2011

I have a TC Electronic BMC-2 monitor controller, that I use and like, for controlling volume on nearfield monitors and headphones in my studio.

But it has a usability problem. We talk about usability in software all the time, but here’s an example of a hardware usability problem. Here’s the back panel:

BMC-2 Back Panel

Whose brilliant idea was it to put the headphone jack under the power jack? When I use it, I’m always in front of the unit, reaching over the top to control it. Which one of these jacks is likely to get plugged and unplugged more often?

Maybe the BMC-2mkII will reverse the positions of these. I doubt it will be a free downloadable update though.

Opinions ,

From the Audio Unit Programming Guide

November 4th, 2011

Audio Plug-In manufacturers would do well to read and re-read this paragraph.

Adding Copy Protection

If you choose to add copy protection to your audio unit, it’s especially important to consider the audio unit’s opening sequence. The time for copy protection is during audio unit initialization—not instantiation. Therefore, you put copy protection code into an override of the Initialize method from the SDK’s AUBase superclass. You do not put copy protection code into an audio unit’s constructor.

Here is a scenario where this matters. Suppose a user doesn’t have the required hardware dongle for your (copy protected) audio unit. Perhaps he left it at home when he brought his laptop to a performance. If your audio unit invokes its copy protection on instantiation, this could prevent a host application from opening. If your audio unit invokes its copy protection on initialization, as recommended, the performer could at least use the host application.

Opinions , ,

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 ,

Get Adobe Flash playerPlugin by wpburn.com wordpress themes