Archive for the ‘Apple’ Category

Vienna 2.2.1 available now (and yesterday, and the day before)

Tuesday, December 4th, 2007

It should come as no surprise that Vienna 2.2.1 was released on Sunday. I’ve been too busy the last two days chipping my car out of the ice to write a post here. Release notes are available for the lazy, though real coders read the commit logs.

Contrary to expectations, this is not really a Leopard compatibility release. I would say that Vienna is not yet Leopard ready. On the other hand, I would also say that Leopard is not yet itself ready. So there you have it.

Working without a nib, Part 6: Working without a xib

Sunday, November 25th, 2007

I bear a heavy burden now, for with the ongoing Hollywood writers strike I am the lone remaining source of pablum in the world. Actually, I was on strike too, from the bagel shop. You know I can’t blog without bagels! However, the recent reconciliation of DLR and EVH inspired us to put aside our differences, and I thought the offer of SCO stock options and tickets to a Broadway show was more than fair. Thus, there will be a series finale to Working without a nib. Maybe they’ll even name a space shuttle after my cat.

Speaking of cats — as I am prone to do — there is a new cat on the Mac (though none on the mat). Apple finally released Mac OS X 10.5, code named “Leopard”, followed soon thereafter by Mac OS X 10.5.1, code named “Oops”. These updates to Mac OS X pose a problem for the Nibless project, because the crucial method +[NSObject poseAsClass:] has been deprecated in Leopard. In fact, this method does not work at all in the Leopard 64-bit runtime.

Fortunately, the new Leopard Objective-C runtime provides a nice solution to the problem. In earlier versions of Nibless we used poseAsClass: to override +[NSBundle loadNibNamed:owner:] while still allowing the original method to be called. We can accomplish the same thing in Leopard via a technique that computer scientists have termed the old switcheroo:


Class bundleClass = [NSBundle class];
Method originalMethod = class_getClassMethod(bundleClass, @selector(loadNibNamed:owner:));
Method categoryMethod = class_getClassMethod(bundleClass, @selector(JJ_loadNibNamed:owner:));
method_exchangeImplementations(originalMethod, categoryMethod);

These functions are documented in the Objective-C 2.0 Runtime Reference. It is straightforward to exchange the method implementations; the mind-bending trick is to call the original implementation afterward.


+(BOOL) JJ_loadNibNamed:(NSString *)aNibName owner:(id)owner
{
	BOOL didLoadNib = YES;
	if (aNibName != nil || owner != NSApp)
	{
		didLoadNib = [self JJ_loadNibNamed:aNibName owner:owner];
	}
	return didLoadNib;
}

Before the function method_exchangeImplementations() is called, the method JJ_loadNibNamed:owner: has the implementation defined above, but the method never gets called until that implementation has been given to the method loadNibNamed:owner:, at which point the method JJ_loadNibNamed:owner: has the original implementation of loadNibNamed:owner:, whatever that may be (only Apple and Uri Geller know for sure). We never get caught in an infinite loop, unlike poor Alice and Norman.

The Leopard version of Nibless you’ve long been waiting for — since the second paragraph — is ready for download. If you find it useful, then please send me a bagel. I’ll also accept a doughnut, but not a Danish. Spoon!

CHUD 4.5 moves Processor.prefPane

Saturday, October 13th, 2007

According to Google, CHUD stands for Cinematic Happenings Under Development. However, those of us in the know realize that it stands for Cats Hold Ultimate Domination. Anyway, if you’ve installed CHUD 4.5 you may be wondering why the >bleep< your Processor pane disappeared from System Preferences.

Never fear, it’s not gone entirely. In CHUD 4.4, it was located in /System/Library/PreferencePanes/. In CHUD 4.5, it has been moved to /Developer/Extras/PreferencePanes/. You can find it there and double-click.

Perhaps the move was part of the reorganization of the developer tools for Leopard. Or perhaps they’re just messing with us. See the release notes for … nothing.

BOOLing for Dollars

Sunday, September 30th, 2007

While we’re all aiting-way or-fay eopard-Lay, I’d like to share a pointer that I picked up while mugging a C library. (I have no idea what that means. It seemed witty when I wrote it.) As you know, I’m always ahead of the curve, setting the trends, framing the public discourse. Thus, I should add my 1.2 cents — the dollar is weak, and I’m a little short this month — on a hot topic discussed on the Cocoa-dev mailing list recently (in geological time, anyway): the use of the Objective-C BOOL type.

If you look in the header file /usr/include/objc/objc.h, you can see how BOOL is defined:


	typedef signed char		BOOL;
	// BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C"
	// even if -funsigned-char is used.

	#define YES             (BOOL)1
	#define NO              (BOOL)0

A char type — e.g., char, signed char, unsigned char — is always one byte, i.e., sizeof(signed char) == 1, whereas in most implementations an int type is more than one byte. A byte standardly consists of 8 bits, or 12 nibbles. What happens to the extra bits if you convert an int into a BOOL? According to the wacky rules of C type conversion, the result is implementation-dependent. Many implementations simply throw away the highest bits. (Other implementations recycle them into information superhighway speed bumps.) As a consequence, it’s possible that myIntVar != 0 && (BOOL)myIntVar == NO.

Usually we don’t have to worry about this, because ‘boolean’ operators in C, such as == and !, always return 1 or 0. When we use bitwise operators, on the other hand, the problem does come into play. Suppose, for example, that we’re testing whether the option key is down. The method -[NSEvent modifierFlags] returns a bit field indicating the modifier keys that are pressed, and bit masks can be used to test for specific keys. Consider the following code; there are situations where doSomethingAfterEvent: does something, yet doSomethingElseAfterEvent: does nothing.


	-(void) doSomethingAfterEvent:(NSEvent *)anEvent
	{
		if (anEvent)
		{
			if ([anEvent modifierFlags] & NSAlternateKeyMask)
			{
				[self doSomething];
			}
		}
	}

	-(void) doSomethingElseAfterEvent:(NSEvent *)anEvent
	{
		if (anEvent)
		{
			BOOL shouldDoSomethingElse = [anEvent modifierFlags] & NSAlternateKeyMask;
			if (shouldDoSomethingElse)
			{
				[self doSomethingElse];
			}
		}
	}

It has been suggested on the mailing list that the type conversion could be handled by


	BOOL shouldDoSomethingElse = !!([anEvent modifierFlags] & NSAlternateKeyMask);

or


	BOOL shouldDoSomethingElse = ([anEvent modifierFlags] & NSAlternateKeyMask) != 0;

However, these approaches would only work for single-bit masks. What if we wanted to test both the option key and the shift key?

The point I wish to make here actually has little to do with the BOOL type. (Say what?!?) Bitwise operators are not boolean operators. A boolean operator only returns 1 or 0. A bitwise operator, in contrast, can return any bit field. The proper way to handle a bitmask is to test whether the resulting bit field has the desired value:


	unsigned int myMask = NSAlternateKeyMask | NSShiftKeyMask;
	BOOL isMyKeyComboPressed = ([anEvent modifierFlags] & myMask) == myMask;

Yes, I know Robot Chicken already covered this subject a ha-while ago. I didn’t really care for it.

Do as we say, not as we do

Thursday, August 30th, 2007

From the Coding Guidelines for Cocoa:

Avoid ambiguity in API names, such as method names that could be interpreted in more than one way.

sendPort

Does it send the port or return it?

displayName

Does it display a name or return the receiver’s title in the user interface?

From the Cocoa API:

-[NSConnection sendPort]

-[NSPortMessage sendPort]

-[NSDocument displayName]

-[NSFont displayName]

What I’ve been doing recently

Saturday, July 21st, 2007

I’ve been asked what I do for a living by a number of my loyal fans. For example, two of my parents. (The third doesn’t care.) Deplorably, I receive no residuals from “The Distinguished Gentleman”, so I guess that I’m a working man. I tried working for no pay, but as rewarding as that was, I couldn’t get my landlord to appreciate the value of my contributions to the open source community. Therefore, I had to sell out to The Man.

The man, in this case, was Marko Karppinen. Marko, you Da Man! Paul, you de Man. When I joined Marko Karppinen & Co., the hazing began immediately: my first assignment was to implement full disk encryption. I also had to purify myself in the waters of Lake Päijänne. I must have passed the initiation, though, because Knox 1.5 is here. It kicks ass! (It sure kicked my ass.) If you don’t believe me, you can download and try it yourself free for 30 days. Launch it from the distribution dmg for kicks. Knox makes encrypting your data effortless, disproving the proverb that you can’t put a price on security. In my opinion, Knox is under-priced, and I’m not saying that because I’m financially compensated by MK&C. Actually, I’m not financially compensated by MK&C.

No, Marko isn’t holding my cats hostage for code. I decided to leave the company recently to pursue another opportunity. Plus, the daily commute to Helsinki was kind of long. I left on good terms, hopefully, and I miss those Pyro-maniacs, my fellow MKCKittens (inside joke). They’re a great group of folks! The strangest aspect of my job was that I never met my coworkers, since I quit right before WWDC. D’oh! I like to imagine that the gang at MK&C is exactly like Dethklok. Did anyone see Toki at the Moscone Center? I guess that makes me Pickles, or maybe the rock & roll clown.

It should be noted that according to my careful tests — I checked my gut and Wikipedia — Knox 1.5 was entirely bug free when I finished. By that irrefutable logic, therefore, any problems that you think you may experience with Knox 1.5 are purely imaginary, and you should ask your doctor about Thorazine. I cannot say the same, however, about that whole Master Control Program I wrote, which got a little out of hand.

My other major project at MK&C was Pyro 1.6. If you use Campfire chat, then Pyro will blow your mind. Or anyway, it significantly enhances the experience. Among the new features Pyro 1.6 introduces is instantaneous switching between multiple chat rooms. (I’m partial toward automatic zip-archiving and uploading of folders and packages.) If you don’t use Campfire, you and your company should give it a try. We used Campfire extensively for internal communication and for customer support.

By the way, there are apparently some problems with Pyro under the deceptively-named Safari 3 beta. That’s not our fault, because Apple released it without any advanced notice to developers. The so-called Safari beta is actually an update to the WebKit system framework, affecting any application on your computer that uses WebKit, not just Safari. The Safari beta has caused problems for other apps too, including Vienna and Apple’s own Mail and iChat, so apparently it was a secret within Apple as well, or they didn’t test it enough. Brutal.

Ranting aside, I’d like to thank Marko for letting me hack on his public-facing apps. Not only would I like to thank him, but I shall. Thanks, Marko! I hope that he and everyone else enjoy the results. Now I must move on and take the next step in my goal of total world domination … by cats.

Working without a nib, Part 5: Open Recent menu

Tuesday, July 10th, 2007

Judging from the search phrases in my referrer log and from posts to Apple’s mailing lists, quite a few people are interested in developing Cocoa applications without using nibs. I’ve heard the demand, and you’ll be pleased to learn that the wait is over. I have a sweet solution. Today I’m announcing a Cocoa nibless SDK. You can download the SDK immediately — from the web! Specifically, from my blog. Just load this web page in Safari and select Save As… from the File menu.

(Don’t worry about me. I hear that the Chairman of the Board has a sense of humor. I’m sure that these two large gentlemen at my door are here to convey his appreciation of my wit and to deliver an invitation to lunch.)

(No! No! Stop, please! Not the iPod too! Have mercy!)

At the end of Part 4 of this series, I suggested that we needed to call setValue:@"NSRecentDocumentsMenu" forKey:@"name" to set the Open Recent menu. This is why they call me “Good Ol’ Oftenwrong”. Luckily, if you have a document-based application, Cocoa will generously create an Open Recent menu for you. All you need to do is put a menu item with the action @selector(openDocument:) in pretty much any menu, and Open Recent will magically appear as the next item in the menu. Now that’s a sweet solution!

If you want an Open Recent menu for a non-document app, on the other hand, you need to use an ugly hack. Although it was clear that the NSMenu ivar _name is set to @"NSRecentDocumentsMenu" for the Open Recent menu in a standard Cocoa MainMenu.nib, I couldn’t get the menu to populate with recent items in my nibless app even after setting _name. By pure trial and error, I discovered that you have to call _setMenuName:@"NSRecentDocumentsMenu" rather than setValue:@"NSRecentDocumentsMenu" forKey:@"name". (It was a natural choice after trying setName: and _setName:, which are not implemented by NSMenu.) The method _setMenuName: does set the _name ivar, but apparently it does some other crucial stuff too. Perhaps it asks a favor of the iGodfather. Anyway, I’ve updated my Nibless Xcode project to demonstrate this behavior.

In summary, we have succeeded (by we I mean the royal we) in creating a Cocoa application with a full main menu but without any nib (and without any error messages). For this purpose we’ve had to call two private methods, -[NSApplication setAppleMenu:] and -[NSMenu _setMenuName:], as well as poseAsClass: to override +[NSBundle loadNibNamed:owner:]. Not bad. And it’s taken us less than two months to reproduce what Interface Builder can do in less than two seconds. Isn’t this fun? The hardest part is finished, though. From now on, it’s just smooth sailing, on the Good Ship of Pyaray.

Oh, one more thing. Let’s dance! Anyway you want it.

Working without a nib, Part 4: setAppleMenu

Sunday, June 17th, 2007

Wow, this series already has four parts. It’s entering Rocky territory, having already surpassed Rambo. If I continue much longer I’ll be typecast — (NSNib *)jeff — and no one will remember my work on Vienna or my poignant portrayal of Hamlet (in the community theater production of Guys and Dolls).

A comment by Jack Nutting inspired me to run some additional tests. As the-programmer-currently-known-as-j-o-a-r (formerly known as horse-spear) mentioned in another comment, you can use the method -[NSApplication setAppleMenu:] to set the application menu. The catch is that it’s been removed from the public API and is no longer declared in NSApplication.h. To call the method, you have to declare it yourself in a category:


@interface NSApplication (NiblessAdditions)
-(void) setAppleMenu:(NSMenu *)aMenu;
@end

You could also avoid the need for a category declaration by using [NSApp performSelector:NSSelectorFromString(@"setAppleMenu:") withObject:aMenu].

My tests revealed that AppKit does call -[NSApplication setAppleMenu:] when loading the main menu from a nib, so it appears that this method is no more (or less) fragile than the technique I discovered in Part 2. As far as I can tell, setAppleMenu: sets the title of the menu to @"Apple" and the name of the menu to @"NSAppleMenu", which is why my technique is functionally equivalent. (The method -[NSApplication setMainMenu:] sets the name of the menu to @"NSMainMenu", so my [mainMenu setValue:@"NSMainMenu" forKey:@"name"] isn’t strictly necessary.) It’s a mystery why Apple ‘disappeared’ the method. Maybe it will magically return in a later operating system, with a long beard and a dark tan.

I’m not aware of a method, hidden or otherwise, to set the recent documents menu. Thus, my technique of calling [openRecentMenu setValue:@"NSRecentDocumentsMenu" forKey:@"name"] still seems necessary. Chasing around a little chicken, though, just seems unnecessary and not very mature.

Working without a nib, Part 5: No, 3!

Sunday, June 10th, 2007

For all you desperate souls waiting in line at the Moscone Center, and you more desperate souls waiting in line at MacRumors, take heart, because there’s something even more desperate than you — NSApplicationMain(). It’s so desperate to load a nib that it’ll take the first one it can find. When your application launches, NSApplicationMain() instantiates the NSPrincipalClass from your app’s Info.plist and calls +[NSBundle loadNibNamed:owner:] with the instance as the owner. This method in turn calls +[NSBundle bundleForClass:] with your NSPrincipalClass and -[NSBundle pathForResource:ofType:] with type @"nib". If your Info.plist contains no NSMainNibFile key, then the nib name and path arguments for those methods are nil. Why in the world would your Info.plist be missing NSMainNibFile? See Part 1 of this series. If that doesn’t answer the question, see Part 2. If that doesn’t answer the question, see Part 3.

When I set the NSPrincipalClass key to my custom NSApplication subclass, the corresponding bundle for that class is my app’s main bundle, so if there’s no nib in the bundle, the app fails to launch with the error, No NSMainNibFile specified in info dictionary, exiting. However, when I leave NSPrincipalClass as NSApplication, the corresponding bundle turns out to be /System/Library/Frameworks/AppKit.framework. If you send the message -[NSBundle pathForResource:nil ofType:@"nib"] to that bundle, it returns /System/Library/Frameworks/AppKit.framework/Resources/English.lproj/NSAlertPanel.nib, which is the first nib file in the English.lproj folder. As a consequence, NSApplicationMain() attempts to load NSAlertPanel.nib and set the file’s owner to your app’s NSApplication instance. That particular nib file contains several buttons with the action buttonPressed: targeted at the file’s owner, but unlike NSAlert, which is specified as the class of the file’s owner in the nib, NSApplication doesn’t implement buttonPressed:, so you get the error, Could not connect the action buttonPressed: to target of class NSApplication. Mystery solved! And I would have gotten away with it too, if it wasn’t for those meddling kids!

There are a number of ways to handle this problem. My preferred workaround, which I’ve implemented in the revised version of the Nibless project, is to set NSPrincipalClass to JJApplication, call [[JJBundle class] poseAsClass:[NSBundle class]] in main.m, and override an NSBundle method in JJBundle.m:


+(BOOL) loadNibNamed:(NSString *)aNibNamed owner:(id)owner {
    if (!aNibNamed && owner == NSApp) {
        // We're lying here. Don't load anything.
        return YES;
    } else {
        return [super loadNibNamed:aNibNamed owner:owner];
    }
}

We now return to our regularly scheduled WWDC speculation. (I predict that everyone in the audience will get a car.) If you are attending The Keynote on Monday, remember to bring plenty of Scooby snacks. If you’re playing the home game: every time Steve says “cool”, drink!

Working without a nib, Part 2: Also Also Wik

Monday, June 4th, 2007

I apologize for the fault in the subtitle. Those responsible have been sacked. What I intended to say was that I’ve discovered the Holy Grail of Cocoa hacks: how to create a functional Cocoa application without any nib. In Part 1 of this series, I suggested that you needed a nib for the main menu of the app; some would even argue that having a nib is the essence of a Cocoa app. It turns out that I was wrong, and some (they, the unspecified straw persons) were wrong too. I apologize for any faults in my previous post. Those responsible would be sacked, but those responsible for sacking have just been sacked.

As you may recall if you have a photographic memory or nothing better to do, the main problem with getting rid of nibs is setting the application menu. I had been using a minimal nib as a workaround, but now I have a reliable, though undocumented, solution to the problem. I came upon the solution by using gdb and JJApp (along with a herring) to override initWithCoder: while the main menu is loaded from a nib. The class NSMenu has a private ivar _name, declared in /System/Library/Frameworks/AppKit.framework/Headers/NSMenu.h, that is usually nil, but a few menus — including the application menu — have a string value for the ivar. Although there are no public accessors, we can set the value anyway through the magic of key-value coding. For example, I called setValue:@"NSMainMenu" forKey:@"name" on my main menu and setValue:@"NSAppleMenu" forKey:@"name" on my application menu. This must be done before the return of -[NSApplication finishLaunching], otherwise it will have no effect. I find applicationWillFinishLaunching: to be a good place to set your main menu.

Another caveat is that the old deleting-the-nib trick doesn’t work at all if you change NSPrincipalClass in Info.plist. When I set it to my NSApplication subclass, the app refuses to launch, complaining, No NSMainNibFile specified in info dictionary, exiting. Strangely, it works fine if I leave NSPrincipalClass alone and call [[JJApplication class] poseAsClass:[NSApplication class]] in main.m. A special treat is that Cocoa automatically adds the Special Characters item to the Edit menu at runtime. Thanks, Cocoa! You can see all of this yourself by downloading my sample Xcode project, Nibless. If you use my code in your app, I may sue you, or I may kiss you. In either case, it’s a risk you’ll have to take. (It!)

Despite the fact that my solution is for the most part unsupported by official API (consult the book of armaments!), there’s good reason to think that it’s stable and should survive Leopard at least. Apple is unlikely to remove an ivar from such an important class as NSMenu. More important, the value of the ivar seems to be the way that archived menus indicate their function to Cocoa and to Interface Builder, as reflected by the “Special Menus” setting in the Inspector. Try opening keyedobjects.nib with BBEdit, and you’ll see that the file is just a plist containing definitions of the objects in the nib.

In Part 5 of this series — sorry, Part 3 — I’ll investigate the cause of the log message Could not connect the action buttonPressed: to target of class NSApplication when launching without a nib. I’ll also attempt to populate the Open Recent menu. The Clear Menu item seems to work, but for some reason I can’t get items to appear in the menu. So, um, anything that you could do to help would be very helpful.

I’d like to end this post on a personal note. Many of my legions of fans have sent emails asking for more information about me: birth date, hobbies, pet peeves, dress size, etc. I prefer not to start a cult of personality, but I’ve decided that you, the heroes, deserve something. Thus, I’m going to share one particularly juicy tidbit. My favorite color is … blue. No, yellooooooooow!