Vincent Gable’s Blog

December 19, 2008

Undocumented Automator.framework Goodness: Actions Library

In Leopard, Apple introduced AMWorkflowView and AMWorkflowController, which let you easily add automator-editing capabilities to your application (modulo bugs!)

Obviously, for editing a workflow to be useful, you need to be able to add actions to it. And to do that, you need to be able to browse and search actions. But the current Automator.framework has no official support for doing this.

But there are undocumented private APIs, which Automator.app (link launches it) uses…

How to Find Them

class-dump is an amazingly useful command-line tool that lets you generate headers from a compiled Objective-C binary. Download it, and point it at /System/Library/Frameworks/Automator.framework/Automator, to see everything Automator.framework really lets you do.

Instant Library Panel

The most useful definition I found was,
@interface NSApplication (AMLibraryPanel)
- (void)orderFrontAutomatorLibraryPanel:(id)sender;
@end

Calling [NSApp orderFrontAutomatorLibraryPanel:nil]; will show a panel with the same action-library view Automator has.

This is what I’m using right now in IMLocation to let people find actions for workflows. I don’t like the idea of using private APIs. But I like the idea of implementing my own AMLibraryView replacement even less. It would be a lot of work to make it as good as the real thing. Any differences give users a fractured experience — one way to do something in Automator, another in my program. And I would get dismal ROI, because I expect Apple to expose this functionality in the (near) future.

(I also wouldn’t be so bullish on using private APIs if I didn’t have workarounds in place right now. The only way orderFrontAutomatorLibraryPanel: gets called is if a user presses a “Show Actions” button on the toolbar. That same toolbar has an “Open in Automator” button that opens the worklfow in Automator, where they can edit it without bugs. So even if orderFrontAutomatorLibraryPanel: stopped working tomorrow, users could still do everything they could before — albeit less elegantly).

Looking Just Like Automator

I don’t recommend doing this, but I’ve been able to embed an AMLibraryView in an NSSplitView next to an AMWorkflowView, to get a more Automator.app-like appearance.

Here’s how I did it, given the IBOutlets workflowViewAndLibrarySplitView which is an NSSplit view with an AMWorkflowView in one side, and the other side’s view connected to the outlet workflowLibraryView,

[workflowViewAndLibrarySplitView replaceSubview:workflowLibraryView with:[[AMLibraryPanel sharedLibraryPanel] _libraryView]];

Will put the library view inside your split view.

Of course this screws up orderFrontAutomatorLibraryPanel:. And I would not expect it to work in two windows. And it’s using an underscore-private method of an already private and undocumented API. That’s just to too risky for me; so I stick with the panel. It gets the job done with one line of code, and that’s good enough for me.

December 18, 2008

Automator Bugs

I’ve started using Automator in IMLocation to let people build a set of actions that are executed when they arrive somewhere (for example, muting their laptop’s speakers in a classroom).

Unfortunately, my impression of Automator.framework so far is: buggy.

Here are some issues I’ve run into so far.

-[AMWorkflowController stop:] does not stop the workflow! I do not yet have a workaround.

AMWorkflowController sends its delegate a -document message whenever an action is removed from the workflowView, by clicking the (x) button. If the delegate does not implement it, the action will not be removed!

The (x) button is enabled , but does nothing, in workflows that are not editable.

-[AMWorkflow initWithContentsOfURL:error:] throws an exception when the URL is nil.

This project demonstrates the first two issues.

December 16, 2008

isEmpty?

Checking if a Cocoa object is empty is a little harder then in other languages, say C++, (but easier in some ways). Because every object in Objective-C is actually a pointer to an object, there are two ways, obj, can be empty.

obj = {}

obj points to an object that is empty. Say an array with 0 items, or the string "", etc..

obj = nil

obj, the pointer obj, is NULL, nil, 0, or whatever you want to call it. You might argue that obj isn’t really an object, but it is empty, because there’s nothing in it.

Bug:

When I first started writing Objective-C, I made the mistake of writing code like: if([name isEqualToString:@""]){ ... }, to test for empty strings. And this code would work for a while until I used it in a situation where name was nil, and then, because sending any method called on nil “returns” NO, I would have mysterious errors. (Worse then a crash, because it’s harder to track down.)

Bug:

It’s tempting to avoid the previous bug, by explicitly testing for nil and {}. Say with code like:

if (email == nil || ![email isEqualTo:@""] )
   email = @"An email address is required";

But generally this is a bad idea. It means more code, which means more places for a bug. I know it’s only one trivial test, but I’m serious, when I say it’s asking for a bug — like the bug in the example above, which sets email to @"An email address is required", whenever it is not the empty string, rather then when it is empty. (Values have been changed tho protect the innocent but it’s a bug I’ve seen.)

Solutions:

Wil Shipley suggests using the global function:

static inline BOOL IsEmpty(id thing) {
    return thing == nil
        || ([thing respondsToSelector:@selector(length)]
        && [(NSData *)thing length] == 0)
        || ([thing respondsToSelector:@selector(count)]
        && [(NSArray *)thing count] == 0);
}

I’ve been using his IsEmpty() for about a year. I’ve had zero problems with it, while it’s made my code more readable and concise.

Another solution is to take advantage of what happens when you send a message to nil. (To over-simplify, you get back 0 or NO.) So you can just say “if ([obj count] == 0) then obj is empty.” This often means reversing your thinking, and testing “IsNotEmpty()” instead of “IsEmpty()”. I don’t think it’s as clear is IsEmpty() in general, but in cases where it is, there you have it.

December 12, 2008

One assert() You Need?

Accidently disconnected outlets in shipping Cocoa apps are legend.

Jonathan “Wolf” Rentzsch

From what I can tell assert() is slowly going the way of the goto in the programming world. Exceptions, unit-tests, and other modern software engineering practices, seem to have a better answer for testing something at runtime, and ensuring that you never enter a bad state.

But there is one case where I think you should put an assert() (and not an NSAssert()) in your code: in awakeFromNib assert() that every IBOutlet is connected.

It’s surprisingly easy to accidentally disconnect something in Interface Builder, or rename something in Xcode. I’ve done it before. More then once. assert()-ing IBOutlets has saved me a lot of debugging time.

Hard to Find In Code

An IBOutlet that is not connected is nil. Because Objective-C quietly ignores messages sent to nil, it’s very easy not to notice the problem for a while; then spend a long time debugging a side effect of the issue.

Hard to Find By Eye

A widget in a nib/xib file that is not connected will never change it’s state. But it typically it has a reasonable initial state. This makes it difficult to detect disconnected IBOutlets by eye, because things will look right until they are supposed have changed, but the eye is drawn to change, and de-emphasizes unchanging things.

Why assert()?

As Wolf says, the worst-case-scenario is that right before release you make a trivial change in Interface Builder (“We can’t ship with that window saying “Claculator”), an IBOutlet gets disconnected, and nobody notices in time.

But an assert() failing will be noticed with casual testing, and triggered if the nib is loaded at all, even if the series of interactions needed to cause the widget to change state are not performed.

An assert() is very light-weight and easy to do. It’s exactly one line of code, and you don’t have to add a unit testing framework to your project to do it. And that makes it perfect for test projects. I’m a believer in getting a feature working in a test project first, then copying it into your real product. (Honestly I don’t do it as much as I should, and I almost always kick myself for it.) It lets you test and learn without hacking-up your product. Plus, new things are often easier to try without the weight of a big code base.

Speaking of unit tests, I don’t have complete faith in them here. That’s because a disconnected IBOutlet in a shipping program is fundamentally an issue with the way the release-build is configured. So anything that’s not testing the the actual release build is not exhaustively testing for this problem. an OCUnit-test can catch a disconnected IBOutlet if it is loaded into the application. But in my experience, such tests are cumbersome. Running them involves launching your full application, and programatically manipulating it. This can take a while to run since all the UI will be displayed and animated. And you have to be careful about state since each test will be changing the state of your program.

Ultimately, assert() just seems to be optimal, in dependability and simplicity, for catching a disconnected IBOutlet.

But I suspect there’s a better way I don’t know.

How do you catch IBOutlets?

December 4, 2008

NSAssert Considered Harmful

Filed under: Bug Bite,Cocoa,MacOSX,Objective-C,Programming,Tips | , , , ,
― Vincent Gable on December 4, 2008

The NSAssert() function is unreliable, and therefore dangerous. Plain-old assert() is a better choice.

Note that #define NDEBUG disables assert(); and #define NS_BLOCK_ASSERTIONS disables NSAssert(). One kind of assert being disabled does not mean the other kind is. Nether kind of assertion is disabled by default when building in “Release” mode — you must be sure to disable them yourself. Be careful, a user will not appreciate the difference between a bug and a failed assertion — both are a crash.

assert() immediately stops your program with a SIGABORT signal. You can’t miss a failed assert(), its behavior is predictable, and it fails fast (link is a PDF).

But a failed NSAssert() behaves unpredictably. Sometimes it stops program execution, sometimes it does not and leaves the program in a strange inconsistent state. I have entirely missed NSAssert()s failing, because I did not look at the console. if(!expr) NSLog(); would have been a better choice in the those cases, because at least I would have known how it behaved.

According to Apple,

Assertion macros, such as NSAssert and NSCAssert … pass a string to an NSAssertionHandler object describing the failure. Each thread has its own NSAssertionHandler object. When invoked, an assertion handler prints an error message that includes the method and class (or function) containing the assertion and raises an NSInternalInconsistencyException.

Complicated! The complexity means it is possible to customize what happens when an NSAssert() fails. That sounds cool, but I’ve never heard of someone needing to actually do that.

If a framework catches NSInternalInconsistencyExceptions, then your program will keep right on running after a failed NSAssert(). I have had this happen to me several times. I apologize for not having taken the time to investigate what frameworks were catching what.

Apple could change what catches what with any software update.

Variability and complexity are the last things you want while debugging. There’s no reason to invite it them by using NSAssert() over assert(). Since NSAssert() is not guaranteed to stop your program, it can not be relied on to guard against data corruption, or anything worse then a predictable crash.

UPDATE 2009-06-01: You can annotate assert(), so it prints an explanation like NSAssert(), by &&ing in a string after the condition. For example assert(i < j) is a lot more useful with an explanation: assert(i < j && "unexpected cycle in graph") — on failure it prints

Assertion failed: (i < j && “unexpected cycle in graph”), function WillHalt(), file /path/to/code.m, line 30.

Cocoa Coding Style Minutia: All Forward Declarations on One Line is the One True Way

This is a very petty point, but I believe there’s a right answer, and since it’s come up before I’m going to take the time to justify the best choice.

Forward Declare Classes on One Line, Not Many Lines

Right:
@class Foo, Bar, Baz...;

This is the style that Apple’s headers generally follow, look at NSDocument.h for example.

Wrong:
@class Foo;
@class Bar;
@class Baz;
...

Programming languages are to be read by people first, and interpreted by computers second. I really do hope anyone in software engineering can take that as an axiom!

Therefore, always favor brevity above all else with statements that are only for the compiler. They are by far the least important part of the code base. Keeping them terse minimizes distractions from code that can and should be read.

Naturally, it’s very hard to find a statement that’s only for the compiler. I can only think of one such statement in Objective-C, forward declarations of an object with @class.

What Does @class Do?

@class Foo; tells the compiler that Foo is an Objective-C class, so that it knows how much space to reserve in memory for things of type Foo. It does not tell the compiler what methods and ivars a Foo has, to do that you need to #import "Foo.h" (or have an @interface Foo... in your .m file.)

@class Foo; #import "Foo.h"
Foo is a black box. You can only use it as an argument. You can also send messages to Foo.

What is @class Good For?

Since #import "Foo.h" does everything @class Foo; does and more, why would you ever use @class? The short answer is less time wasted compiling.

Lets say you have a Controller class, and it has an ivar that’s a Foo. To get it to compile, you put #import "Foo.h" inside Controller.h. So far so good. The problem comes when Foo.h is changed. Now any file that has #import "Foo.h" in it must be recompiled. So Controller.h has to be recompiled. So any file that has #import "Controller.h" in it must also be recompiled, and so on. Many objects that don’t use Foo objects, but do talk to the Controller (or to something that talks to something that talks to the Controller!) have to be rebuilt as well. It’s likely that the entire project would end up being rebuilt. With even a moderately sized project, that means a lot of needless compile time!

One solution is to put a forward-declaration, @class Foo;, in Controller.h, and #import "Foo.h" in Controller.m, and the few files that actually do something to Foo objects. @class Foo; gives the compiler just enough information to build an object with a Foo* ivar, because it knows how much space that ivar will need in memory. And since only objects that need to talk to Foo objects directly have any dependency on Foo.h, changes to Foo can be tested quickly. The first time I tried this solution in one of my projects, compile times dropped from minutes to seconds.

Readers Need Less @class

Forward declarations add value to the development process, as does anything that saves programmers time. But @class tells a human reader nothing. Which is why I say you should use them in your code, but minimize the space they take up, by putting them all on one line.

@class Foo; tells a reader that “Foo is black-box that is a class.” That adds no useful information.

If the reader does not care about what a Foo is (they are treating it as a black box), then knowing that Foo is a class is useless information. Foo could be a struct, or a bit-field, or a function pointer, or anything else and they still would not need to know to understand the code.

Conversely, if they need to know what a Foo is to understand the code, then they need to know more then “it is a class”. They need to see Foo.h or documentation — @class just isn’t enough.

Exceptions

I want to again emphasize that this isn’t a big deal. I strongly feel that putting all forward declarations on one line is the right answer, but doing it wrong won’t perceptibly matter in the end.

So I don’t think there’s any reason to address exceptions to the rule. Do what you gotta do.

Best Practices

Use @class to speed up compile-times as much as you can, but use the keyword @class as little as possible, since it is for the compiler, not the person reading the code.

November 24, 2008

How To Put a % in an NSString/NSLog/printf

Filed under: Cocoa,MacOSX,Objective-C,Programming | , , , , , ,
― Vincent Gable on November 24, 2008

%% is turned into a single % in a call to NSLog, or -[NSString stringWithFormat:], or the printf-family of functions.

Note that %%format will become %format, even if %format usually prints an argument. For example, the code

NSLog(@"%%a will print a float in a machine-readable format, so that *scanf can read it back in from a string with no loss of precision.", 1.0f);

prints:

%a will print a float in a machine-readable format, so that *scanf can read
it back in from a string with no loss of precision.

not:

%0x1p+0 will print a float in a machine-readable format, so that *scanf can read it back in from a string with no loss of precision.

November 14, 2008

Prefer copy Over retain

Filed under: Bug Bite,Cocoa,Objective-C,Programming | , ,
― Vincent Gable on November 14, 2008

(Almost) every time you use retain in Objective-C/Cocoa, you really should be using copy. Using retain can introduce some subtle bugs, and copy is faster then you think…

A Bug Waiting To Bite

The problem with using retain to “take ownership” of an object is that someone else has a pointer to the same object, and if they change it, you will be affected.

For example, let’s say you have a Person class with a straightforward setter method:
- (void) setThingsToCallTheBossToHisFace:(NSArray*)newNames {
   [thingsToCallTheBossToHisFace autorelease];
   thingsToCallTheBossToHisFace = [newNames retain];
}

And you use it to initialize a few Person objects:

NSMutableArray *appropriateNames = [NSMutableArray arrayWithObject:@"Mr. Smith"];
[anIntern setThingsToCallTheBossToHisFace:appropriateNames];

//Salaried Employees can also be a bit more informal
[appropriateNames addObject:@"Joe"];
[aSalariedEmployee setThingsToCallTheBossToHisFace:appropriateNames];

//the wife can also use terms of endearment
[appropriateNames addObject:@"Honey"];
[appropriateNames addObject:@"Darling"];
[theBossesWife setThingsToCallTheBossToHisFace:appropriateNames];


The code looks good, it compiles without error, and it has a bug in it. Because setThingsToCallTheBossToHisFace: uses retain, each Person object’s thingsToCallTheBossToHisFace field is actually pointing to the exact same NSMutableArray. So adding “darling” to the list of names the wife can use also adds it to the intern’s vocabulary.

If copy was used instead, then each Person would have their own separate list of names, insulated from changes to the temporary variable appropriateNames.

A Sneaky Bug Too

This is a particularly insidious problem in Foundation/Cocoa, because mutable objects are subclasses of immutable objects. This means every NSMutableThing is also a NSThing. So even if a method is declared to take an immutable object, if someone passes in a mutable object by accident, there will be no compile-time or run-time warnings.

Unfortunately, there isn’t a good way to enforce that a method takes an object, but not a subclass. Because Foundation makes heavy use of class clusters, it’s very difficult to figure out if you have an immutable class, or it’s mutable subclass. For example, with:
NSArray *immutableArray = [NSArray array];
NSMutableArray *mutableArray = [NSMutableArray array];

[immutableArray isKindOfClass:[NSArray class]] is YES
[immutableArray isKindOfClass:[NSMutableArray class]] is YES
[mutableArray isKindOfClass:[NSArray class]] is YES
[mutableArray isKindOfClass:[NSMutableArray class]] is YES
[mutableArray isKindOfClass:[immutableArray class]] is YES
[immutableArray isKindOfClass:[mutableArray class]] is YES

Sad, but true.

copy Is Fast!

With nearly every immutable Foundation object, copy and retain are the same thing — there is absolutely no penalty for using copy over retain! The only time you would take a performance hit using copy would be if the object actually was mutable. And then you really do want to copy it, to avoid bugs!

The only exceptions I know of are: NSDate, and NSAttributedString.

But don’t just take my word for it! Here’s the snippet of code I used to test all this:

NSMutableArray *objects = [NSMutableArray array];
//add anything that can be made with alloc/init
NSArray *classNames = [NSArray arrayWithObjects:@"NSArray", @"NSColor", @"NSData", @"NSDictionary", @"NSSet", @"NSString", nil];
for(NSString *className in classNames) {
   id obj = [[NSClassFromString(className) alloc] init];
   if(obj)
      [objects addObject:obj];
   else
      NSLog(@"WARNING: Could not instatiate an object of class %@", className);
}

//manually add objects that must be created in a unique way
[objects addObject:[[NSAttributedString alloc] initWithString:@""]];
[objects addObject:[NSDate date]];
[objects addObject:[NSNumber numberWithInt:0]];
[objects addObject:[NSValue valueWithSize:NSZeroSize]];

//test if retain and copy do the same thing
for(id obj in objects)
   if(obj != [obj copy])
      NSLog(@"copy and retain are not equvalent for %@ objects", [obj className]);

Best Practices

Get in the habit of using copy, anytime you need to set or initWith something. In general, copy is safer then retain, so always prefer it.

I believe it is best to try copy first. If an object can not be copied, you will find out about it the first time your code is executed. It will be trivial to substitute retain for copy. But it is much harder, and takes much longer, to discover that you should have been using copy instead of retain.

A program must be correct before it can be made to run faster. And we have seen there is no performance penalty for copy on most common objects. So it makes sense to try copy first, and then replace it with retain if it proves to be necessary through measurement. You will be measuring before you start “optimizing”, right? (I also suspect that if taking ownership of an object is a bottle-neck, then the right optimization is not to switch to retain, but to find a way to use a mutable object, or an object pool, to avoid the “take ownership” step altogether.)

Choose copy, unless you have a measurable justification for using retain.

UPDATE 2009-11-10: Obj-C 2.0 blocks have some peculiarities,

For this reason, if you need to return a block from a function or method, you must [[block copy] autorelease] it, not simply [[block retain] autorelease] it.

October 29, 2008

Two Help Menus

Filed under: Bug Bite,Cocoa,Interface Builder,MacOSX,Objective-C,Programming | , ,
― Vincent Gable on October 29, 2008

I ran into an issue at work where sometimes an application would have two “Help” menus, on OS X 10.5 “Leopard” (but not on OS X 10.4 “Tiger”). The problem was interacting with the UI before the application had finished enough of the AppKit-initialization process.

The application had to install a component, which involved displaying authentication dialogs and such. Because the component was necessary for the application to work correctly, I thought it would be safest to do this as early as possible. But displaying a dialog in code called from awakeFromNib ended up being the cause of the double Help Menu issue.

Waiting until applicationWillFinishLaunching: or applicationDidFinishLaunching: to interact with the user fixed the problem. (In my case, it was safe to defer the installation until then.)

I am not aware of any other issues from putting up a window “too early” … however, it seems to me that doing it is asking for trouble. The AppKit/Cocoa environment obviously isn’t 100% ready at that point. Why risk running your code with half-baked libraries if you don’t have to?

Whenever possible, I will defer “first run” behavior until applicationWillFinishLaunching: or applicationDidFinishLaunching:.

October 8, 2008

Drawing the floor()

Filed under: Bug Bite,Cocoa,Design,MacOSX,Objective-C,Programming | , , , , ,
― Vincent Gable on October 8, 2008

So when drawing a custom-Cocoa-control, I had an issue where an NSImage (bitmap) and an NSBezierPath (vector-shape) didn’t move in sync with each other.

Using floor() to force all pixel-coordinate to be a whole number fixed the problem. There is probably a much better solution — after-all this was the first custom-control I have ever done in Cocoa. But when a simple solution works…

UPDATED: 2009-05-25: @cocoadevcentral tweets,

NSIntegralRect() and CGRectIntegral() take a rect and return a copy which is aligned to integer coordinates. Good to avoid blurriness.

They sound like a better solution then calling floor() on each element of an image.

« Newer PostsOlder Posts »

Powered by WordPress