/dev/trouble
Eric Roller's Development Blog

While searching for the culprit that is responsible for the AirTunes cut-outs, I thought it might be a good idea to try disabling Spotlight and Dashboard.

Two steps are necessary, first turn Spotlight off in the hostconfig file, then disable indexing and remove the existing indexes with mdutil:

> sudo vi /etc/hostconfig
vi> Go
SPOTLIGHT=-NO-
vi> ZZ
> sudo mdutil -i off /
> sudo mdutil -E /

You can then use Activity Monitor to kill any mds or mdimport processes or else just reboot.

If you later change your mind and decide you want Spotlight after all, it's easy to reactivate. Set SPOTLIGHT=-YES- in /etc/hostconfig, run in Terminal:

> mdutil -i on /</pre>and reboot.

Disabling Dashboard

To turn Dashboard off:

> defaults write com.apple.dashboard mcx-disabled -boolean YES

To turn Dashboard on:

> defaults write com.apple.dashboard mcx-disabled -boolean NO

You have to restart the Dock after making either change for it to take effect:

> killall Dock

(NB. Neither turned out to be the culprit for AirTunes. It was the broadband router which dropped more and more packages until it died a couple of months later.)

Encountering the above Xcode error when trying to build a Unit Test Bundle target usually means one of two things:

  • The SenTestingKit is not installed.
  • You are using an SDK with an older version of the Mac OS, where SenTestingKit is not included.

For the latter case, a possible solution is to set SDKROOT to "" (an empty string) for the testing target. Alternatively, one may add a link to the framework inside the /Developer/SDKs/<version>/System/Frameworks directory. However, trying that only triggered the next problem:

...MyApp: [NSBundle load] ... EXC_BAD_ACCESS (0x0001)KERN_INVALID_ADDRESS (0x0001) at 0xfffffffc /Developer/Tools/otest exited with error code 11 (it may have crashed)

It turns out that one should not try to run a unit test framework with settings other than ARCHS=$(native_arch), i.e. on a MacBook Pro, it should be "i386".

A follow-up problem is that one cannot test items with third-party libraries that do not support the same architecture (e.g. libeSellerateObjC.a, version 3.6.3, is only "ppc").

Enter this in the Terminal application:

defaults write com.apple.desktopservices DSDontWriteNetworkStores true

This tip originally surfaced for Mac OS X 10.4 Tiger, but may also work for 10.5 Leopard.

Use of dealloc

- Posted in macOS by

After spending a few days trying to figure out why my log file was not truncated correctly, I re-discovered a feature of the Objective-C runtime: The dealloc method is never called - at least not to my knowledge.

Therefore, do not add mission-critical code in the dealloc method of a class; use a dedicated custom method instead, for instance:

- (void) close
{
    [theLog truncateFileAtOffset:[theLog offsetInFile]];
    [theLog closeFile];
}

Splitting Disk Images

- Posted in General by

This has been bugging me for a while: How do you split large .dmg files such that you can save them onto multiple CDs (as opposed to an expensive DVD). It turns out, there is no special tool required (although it would be nice if Disk Utility would do the job). No, the functionality is already built-in in the Mac OS. All you need to do is to use the hdiutil in the Terminal to create your partitions:

% cd Downloads
% mkdir parts
% hdiutil segment -o ./parts/large_image.dmg \
    -segmentSize 100m large_image.dmg

Of the resulting files in the parts directory, one can simply double-click the first one in the Finder to mount the entire image:

large_image.dmg
large_image.002.dmgpart
[...]
large_image.010.dmgpart

A not very well documented error message which occurs right at the beginning of the build, typically during compilation of the prefix file. The error is found in a derived file, for instance:

.../EnterMordor.build/DerivedSources/EnterMordor_vers.c:1: error: parse error before ';' token:

[...] const double EnterMordorVersionNumber __attribute__
((used)) = (double);

Observe that there is no valid argument after the equal sign. To find the cause, we must know that derived files are based on our build settings. In my case, the fault lies in the fact that the built setting VERSIONING_SYSTEM is set to apple-generic, but I was missing is a setting for the CURRENT_PROJECT_VERSION variable.

See also Xcode: Help -> Show Build Setting Notes -> Versioning.

According to Chris Hanson's article, the apple-generic versioning system uses agvtool to auto-update all Info.plist files. Also, there will be a TargetVersionString and a TargetVersionNumber variable available to your code. Right now, I you do not like or need all that, so I simply set the versioning system to "none" and continue to manually update the CFBundleShortVersionString and CFBundleVersion settings.

Using a popup menu to allow the user to select the (integer) value of an object can be easily done through binding the selectionIndex. However, what if you wanted to launch an update method when the selection changes, you have two options:

  • Assign a target to each menu item
  • Assign a target to the menu itself

I made the mistake of trying the first option, assigning the same target to each of the menu items. This effectively disabled the selectionIndex binding, rendering the popup menu completely useless.

It appears as if the only solution is to assign a target to the menu, not its items!

If you ask me, this is a bug: binding "enabled" of an NSMenuItem to anything does not make any difference. It seems that the target of the menu item needs to tackle the issue in the conventional manner, i.e. through the method:

- (BOOL) validateMenuItem:(NSMenuItem*)item
{
    if (itemShouldBeEnabled)
        return YES;

    return NO;
}

The downside of this is that the target may need access to other objects that would have beeen simple to achieve through the binding mechanism, sigh.

In a custom NSArrayController, an action was only to be done when we had a selection. The initial attempt was along the lines of:

if ([self selection])
{
    // do something
}

That, however, does not work since even an empty selection returns a proxy object:

(gdb) print-object [self selection]
<_NSControllerObjectProxy: 0x5bfa60>
(gdb) print-object [[self selection] valueForKeyPath:@"db.name"]

So I checked the NSObjectController documentation and was reminded that a NSNoSelectionMarker can be returned by the function, making me think it that the if statement could be changed like this (NOT):

if ([self selection] != NSNoSelectionMarker)
{
    // DOES NOT WORK
}

However, this does not work (but checking the return value of valueForKeyPath: might).

The most elegant solution for an NSArrayController would be to ignore the selection and just to check the (first) selectionIndex like this:

if ([self selectionIndex] != NSNotFound)
{
    // do something
}

There does not appear to be a way to easily define a double-click action for an NSTableView in Interface Builder. The best solution that I have found is via a custom table view class and a dummy action. This requires that the target is available in the nib file, e.g. the window controller or a custom array controller of the table view.

Step one: control-drag a target/action-connection from the table view to the target where the double-click action is defined, but select a different action method, at best a bogus method that does nothing (I like to use "noAction:" for such purposes).

awakeFromNib method to assign a double-click action.

+ (void) awakeFromNib
{
    NSAssert1([[self target] isKindOfClass:[Duck class]],
            @"The target of MyTableView should be Duck, not %@.",
            [[self target] class]);

    // Let our drag-and-drop array controller, i.e. the Duck,
    // handle double-clicks to the cells in the table view.
    [self setDoubleAction:@selector(doubleClick:)];

    // We no longer need the dummy action (noAction:)
    [self setAction:NULL];
}