Topics > Blog
Blog

The blog, managed by jediknil himself.

Quickie for today that some people might find handy. (I need to start posting much more. And reorganize a bit.)

Just yesterday I found myself wondering what to do when Dockyard would crash. The new Dockyard uses the wonderfully transparent distributed objects of Cocoa, which become staggeringly destructive when the remote process crashes. If you only used the data in a few places, or transferred everything by-copy, then you can probably recover. But in the bindings-intensive AppKit world of Dockyard Manager, you're going to crash before you can say [[self window] close:nil] (I tried.) The bindings all try to update and come back complaining about a broken connection. The only thing that worked was to bail out, but if I tried to show an NSAlert to tell the user we had to bail out, the app got stuck in the modal loop. I have no idea why, but I can only assume it's because the hordes of orphaned object proxies have destroyed all of the run loops by this point.

(I'd like to know why I can't just use NSAlert, but I have no idea where to go from the bit of poking I did. Pausing during the alert reveals an apparently normal run loop, but the alert simply won't close.)

So, I have decided instead to bail out as planned, but to show an alert after the application quits. (Huh‽) Rather than use AppleScript's "display alert", which doesn't look as nice, requires twice as much space as my final solution, and (the clincher) doesn't work on Panther outside of AppleScript Studio, I wrote a small application intended to be called with a property list file describing the parameters of the alert.

The application wouldn't be so remarkable, except that it works and it's nibless. Even if you don't care about the app itself (to be fair it's really odd to want to display an alert without having an application context already, and a better sudden death app might be needed in the end), you might still be a bit interested in how to write a nibless application.

Summary: nibless faceless application that displays alerts based on a property list file description. If you're interested at all, you can download the source to what I've named Alert Notifier.


Interesting fact of the day. NSNumber and CFNumber are toll-free bridged, right?

Not according to Apple. Nowhere on NSNumber's class page nor in the "Number and Value Programming" guide does it mention that NSNumber and CFNumber are toll-free bridged.

Next question. CFNumber and NSNumber are toll-free bridged, right?

Of course.

Recently I've been working on an outline view property list editor for Dockyard, and I think I have a bit of a clue as to what's going on. Most NSNumber types are instances of NSCFNumber, similar to most of Apple's bridged classes.

But NSNumbers created with BOOL values are instances of NSCFBoolean. And CFBooleans aren't CFNumbers.

So NSCFBoolean implements enough of NSNumber's methods to pass as an NSNumber...and NSNumber subclasses can already pass as CFNumbers. The most definitive toll-free bridging reference, Interchangeable Data Types, does say CFNumber and NSNumber are toll-free bridged. My hypothesis is because all CFNumbers are NSNumbers but not vice versa, some documentation writer decided not to include the usual toll-free bridging footnote.

Even though it should be there. The Property List Programming Guide implies the same thing. Interestingly, while CFBooleans are also valid NSNumbers, not all NSNumbers are valid CFBooleans. Which is why CFBoolean is not "toll-free bridged" with NSNumber, even though it will, in fact, work perfectly well to cast a CFBooleanRef to NSNumber *.

Also interesting is that while CFBooleans are not CFNumbers in the CoreFoundation hierarchy, they can be casted as if they were. Weird, huh?

Lack of bridging info for NSNumber filed as documentation bug #5688009


About a month and a half ago I got an e-mail from a GenericToolbar user telling me that GenericToolbar didn't work with Leopard, or more specifically with IB3. The nib file reported a missing plugin and then wouldn't load because the GenericToolbar class couldn't be found.

Interface Builder 3 does not use IB2 palettes.

At this time I actually did not have Leopard myself, developer preview or otherwise. As such I wasn't able to do anything at the time. When I actually did get Leopard, however, I set to work on the problem immediately (after making sure my most mainstream app, Webmailer, still worked).

Initial triage showed that my original plan would not work; I had intended to make GenericToolbar into a sort of compatibility patch for IB2 to read IB3 toolbars, but the entire nib file format changed. So that was out.

What did have to happen, though, was a way for all those people who had used GenericToolbar in the past to open their nib files, even if it was just to delete the toolbar. (Of course, that's not what I was aiming for.) Sure, I could say "reinstall Xcode 2", but what kind of developer would respect that?

So as of two days ago, the GenericToolbar Converter for IB3 has been released, and GenericToolbar has effectively been retired. The converter is an IB3 plugin that essentially loads IB2 GenericToolbar nibs as if they were using IB3 toolbars. GenericToolbar had slightly more capabilities than IB3 toolbars, so a few things are lost, and there are a few options the user can choose from for how the import actually works.

When I say GenericToolbar has been "retired", I mean that while it is still supported (both the converter and the IB2 palette), I don't plan to add any more features, nor add full support for GenericToolbar in IB3. Apple did a nice enough job as it is, and there's still IB2 around if you need it.

Of course...IB3 toolbars still aren't supported on older versions of OS X. So there may still be a GenericToolbar revived for IB3...if there's enough interest.


As for the rest of the Belkadan product family, I'm in reasonably good shape. Webmailer is good to go, and works just as well as it did in Tiger. (It's a pretty simple app, so I'm not surprised.) The elusive PHP/CC project is on hold for now; it's having some problems loading into Xcode 3. I'm going to try to keep that compatible with Xcode 2 as well.

And Dockyard, supposedly the flagship product of Belkadan Software is—still not ready for Leopard. Third-party menu extras were broken once again on Leopard; I can only hope that the Unsanity folks can get MEE up and running soon. But Dockyard Manager still works. Rather, it still runs. But it doesn't play nicely with Spaces, not at all...since the Spaces preferences are stored with the rest of the Dock preferences. (It would have been nice if Apple had separated them like it did with the Dashboard prefs. Dashboard, by the way, is also run by the Dock, along with Cmd-Tab, Exposé, and good old minimized windows.) Some people consider this a feature (I actually got an e-mail saying as much), but when your old Spaces app-binding preferences don't quite return when you switch back, it's more of a bug than anything else.

So, I'm investigating alternate ways to change the items in the Dock, as well as giving the Dockyard interface a complete overhaul. For that reason a new version of Dockyard won't be coming in the imminent future. But the near future, sure.


Recently I was coding away and found myself needing to access something three levels down in a dictionary. I immediately cringed at the thought of the following:

[[[info objectForKey:@"tile-data"] objectForKey:@"file-data"] objectForKey:@"_CFURLStringType"];

Why? Because it's ugly. I mean, the code to get an object out of a dictionary completely buries which object I'm getting. It would be much nicer just to do this:

[info valueForKeyPath:@"tile-data.file-data._CFURLStringType"]

But I was wary enough to run some speed tests, using the harness written by Mike Ash for his performance tests. The results:

NameIterationsTotal time (sec)Time per (ns)
objectForKey x3100000005.0497.1
valueForKey x3100000005.6561.9
valueForKeyPath1000000071.97186.2

Looks like valueForKeyPath: is over ten times slower than the repeated objectForKey:. That kind of difference is pretty significant, even if you have to run the code 10 million times to see it. (For the curious, the objectForKey: and valueForKey: calls are on the same order as creating and releasing an NSAutoreleasePool; the valueForKeyPath: call a bit slower than creating an NSButtonCell. So it's not that slow either way.)

This is pretty damning evidence. Looks like the prettier syntax is going to be sacrificed before I make it a habit.

Note: I did not make any special effort to remove all other factors in this test, such as length of the individual keys, number of keys in the path, or even what else was running on my computer at the time. (That last, I know, is quite serious, although I did avoid being active.) I simply tested the same dictionary (loaded once for each test, not included in the timing) with the key path given above. If the performance results were closer, I might have done a more precise test, but they weren't, so I won't.


When writing GenericToolbar 1.0 there were plenty of times I wanted to print something to the run log using NSLog, or even just observe the error messages IB prints. So I'd go call up the old Console utility and watch as my autorefreshing webmail window prints line after line, while IB is completely silent and error-less. Or did I just miss it with all the Safari AJAX calls?

It wasn't too bad, but it's not a very pleasant way to debug a plugin. And as for true debugging and breakpoints, forget it.

By the time I was starting work on GenericToolbar 1.1, I had definitely realized that this was an awkward way to develop, and didn't want to use it again. So what's the answer?

Custom executables.

That's right. Thanks to Xcode's Custom Executables function, you can start Interface Builder (or TextEdit, or even another Xcode) inside the run environment! Run logs work just like your own executables, and breakpoints usually function quite well. (I say "usually" because a few times they seem not to, but that could be the nature of what I was trying to do at the time.) So next time you're debugging a plugin, just create a custom executable for the target application and you're good to go.

Side note: there are a few caveats with this method, notably that some applications don't lke to have multiple instances running. Interface Builder will actually freeze (which then freezes the Xcode you're running it in) if a normal IB is already open. And as for menu extras like Dockyard, I'm not sure how you'd get your own SystemUIServer (the app that runs menu extras, among other things) into place when there's already one running since you logged in. Maybe a quick kill/launch would do it, but I haven't tried messing with that so far.

I've got another plugin "ready" besides GenericToolbar, called PHP/CC. It adds PHP code completion and source scanning to Xcode using Xcode's own (undocumented) plugin system and private frameworks. I'm not releasing it publicly until I've used it myself for a month or so and convinced myself that there are no serious bugs.


Next page | Last page | View all

Sort by: Date / Title | Order: Ascending / Descending