Archive for the ‘Apple’ Category

NSScrollView in a key view loop (or Fembot in a wet T-shirt)

Wednesday, February 20th, 2008

Now that the Pats have won the Super Bowl, Rudy has won the GOP nomination, and the Jedi have won the Clone Wars, all is right with the world (and the galaxy), so we can focus again on our lives, on our families, and most important, on Cocoa. If you’re a Cocoa developer, you are morally required to open System Preferences, Keyboard & Mouse, Keyboard Shortcuts and select All controls in Full keyboard access. This is not optional. If you do not comply, you will be driven out of the Continuum and forced to spend the rest of your pitiful mortal existence writing kernel extensions.

With the mandatory preference setting, you will observe the full Cocoa key view loop. Actually, it doesn’t have to be a loop: it could be a key view cul-de-sac. Anyway, Apple clearly wants developers to “leave the driving to us”, i.e.,

-[NSWindow setAutorecalculatesKeyViewLoop:YES]

because configuring and updating the key view loop can be a pita — like training a cat. The docs make it sound oh so easy, but that’s only because the docs don’t cover the complex or problematic cases. Suppose, for example, that you have a scroll view enclosing a view that contains multiple controls. You want the key view loop to follow a particular route both outside and inside the scroll view. How should you hook up the views in Interface Builder? (Or in code, when you’re working without a nib.)

I believe that the proper way to handle an NSScrollView in a key view loop is to ignore it. Walk by quickly without making eye contact. Whatever you do, don’t engage in conversation! Otherwise you’ll get invited to coffee or lunch. The reason you can ignore NSScrollView is that it never actually becomes the firstResponder of an NSWindow. The scroll view returns YES from acceptsFirstResponder if its documentView does, but if you then call -[NSWindow makeFirstResponder:] with the scroll view as argument, the window’s first responder will end up being not the scroll view itself but rather its document view.

This behavior is adequate if the document view is a single control such as an NSTableView or an NSTextView (though maddeningly, an NSTextView tends to interpret the tab key as, well, a tab). In the window nib, you can simply include the scroll view in the window’s key view loop, ignoring the document view, and when the window loads, the scroll view will automatically rearrange the key view loop to make its NSClipView the nextKeyView, followed by the document view and then the scroll view’s original nextKeyView. The scroll view and the clip view are superfluous in the key view loop, however, because only the document view becomes the first responder when tabbing through the window.

This behind-the-curtain key-view wizardry may seem impressive, but the scroll view is really deaf, dumb, and blind. It has no idea about any loop you’ve defined within the document view. There is no way to inform the scroll view of the beginning and end points of the ‘sub-loop’. If you attempt to insert the scroll view containing your sub-loop into the window’s key view loop, it will turn into either a key view dead end within the scroll view or a key view overpass, depending on the configuration.

To avoid these problems, simply connect your views together in the key view loop as if the scroll view did not even exist. You know, like Mac OS X Tiger. (Of course the keyboard firmware update requires Quick Look and Time Machine!) Only the views that become first responder — as opposed to becomeFirstResponder — need to be hooked up. When a cat is lying on the trackpad, you will be thankful for setting the key view loops in your app, and so will your users. Cat-friendliness is in fact the most crucial consideration for designing both software and hardware.

I am saying that I will neither aspire to nor accept — I repeat, I will neither aspire to nor accept — the positions of President of the State Council and Commander in Chief. I will, on the other hand, host the Tonight Show if asked. Hasta la vista!

FUD from Rixstep: NSDocumentController in Leopard

Saturday, February 2nd, 2008

At the risk of provoking their ire and being branded a moron, I wish to dispute a claim that has been made several times by Rixstep about a supposed security vulnerability in Leopard’s NSDocumentController: Cocoa’s document controller overrides file system permissions without authentication. As far as I can tell, this claim is false.

I should note at the outset that I have nothing personal against Rixstep. Their syndicated feed has long been among my (numerous) favorites that I subscribe to in Vienna, as you can see by downloading the exported opml from my blog. I welcome legitimate criticism of Apple, and I have found Rixstep’s articles entertaining in the past, though of late they have become overly juvenile. As far as the whole ‘Cross-Platform Bait & Switch’ incident is concerned, I can’t comment on the legality of reproducing the quotations, because I’m not a lawyer, but I don’t think that Rixstep can be accused of misquoting or quoting out of context in this case.

Anyway, I’ve included the full text below of the email I sent to Rixstep a month ago. Retraction was perhaps too much to hope for, but I thought they would at least stop making the false claim after reading my email. They haven’t, which is why I’m now ‘going public’. (This should knock Britney off the front page.)

Hi. I read your article at <http://rixstep.com/2/1/20071227,00.shtml>, and it got me a little worried, so I did some testing. My results are that NSDocumentController in Leopard does not allow you to override Unix permissions. In fact, NSDocumentController in Leopard is more strict than the Unix permissions: it won’t allow you to save a writable document when you don’t have write permissions for the enclosing directory.

You are correct that saving a document always deletes the existing file and creates a new one with a different inode, and you are also correct that some of the user-visible NSDocumentController warning messages are misleading. However, if you don’t have write permissions for a directory, then NSDocumentController won’t let you delete or add a file, and even if you do have write permissions for the directory, NSDocumentController won’t let you delete someone else’s file or add a file with the same name if the sticky bit is set.

Thus, I believe that the ramifications for system administrators are negligible. With certain Unix permissions, it has always been possible to delete a file in a directory and replace it with a new file of the same name. Leopard has not changed that at all, so system administrators should take the same precautions against this scenario that they have always taken.

-Jeff

You don’t have to take my conclusions for granted, though. In a matter of minutes, you should be able to throw together a bare-bones document-based application suitable for testing the behavior yourself.

I have no desire to discourage criticism of Apple or Leopard. There are major problems in Leopard, and I don’t yet find it acceptable for use as my primary operating system. I just think that this spurious security issue obscures the real issue of whether the new Leopard NSDocumentController behavior is desirable.

P.S. If you really want to talk about a waste of precious disk space, /Applications/Mail.app/Contents/Resources is an astounding 277 MB on Leopard.

Behind the firewall

Saturday, December 29th, 2007

Some people are apparently under the impression that firewall rules are the be-all and end-all of Mac OS X security. Deny all from any to me in. Before you spend days composing the perfect ipfw ruleset, however, take a look at sysctl. (You could spend days looking at sysctl.) One of the biggest misconceptions about Mac OS X — a misconception encouraged by the System Preferences UI — is that the firewall is not enabled out of the box. Nonsense!

net.inet.ip.fw.enable: 1

Admittedly, it’s a fairly flammable firewall (allow ip from any to any), but it’s enabled. More important, the default sysctl settings do provide at least a modest level of protection. For example:


net.inet.icmp.timestamp: 0
net.inet.icmp.maskrepl: 0

These prevent your machine from responding to ping -M time and ping -M mask requests. What time is it? Where the party at?

By Mac OS X herein I shall mean Leopard and Tiger. (Panther is not yet abandonware, but at this point it merely receives Christmas cards.) When you ‘start’ the firewall in System Preferences, Tiger adds a number of rules to ipfw. Leopard, on the other hand, includes a new Apple-designed firewall that mostly replaces ipfw. Though Leopard respects custom ipfw rulesets, it adds no rules to ipfw when you choose to block incoming connections. I don’t know much about how the Leopard firewall works; presumably it protects you somehow or other.

Leopard does still employ ipfw for “Stealth Mode”. If you turn that on, both Leopard and Tiger add the rule deny icmp from any to me in icmptypes 8. Curiously, Stealth mode prevents you from ‘pinging yourself’ in Leopard but not in Tiger. For fun (according to a rather loose definition of fun), try ping localhost. Somebody bring me a mirror! The difference in behavior is due to the Tiger-only rule allow ip from any to any via lo* that precedes all other rules.

Looking at the ipfw rules, you might think that Stealth Mode is overrated, like Notre Dame football and most wines over three dollars. If you want to learn its true value, you need to consult sysctl.


net.inet.tcp.blackhole: 2
net.inet.udp.blackhole: 1

With these settings, your computer mercilessly crushes attackers into singularities, as well as dropping their packets without response when sent to closed ports.

It would be nice if Bonjour wasn’t so talkative, broadcasting its greetings to your entire LAN. Bonjour also complains loudly when told to shut up by the firewall. On the other hand, you’re going to have to broadcast anyway for DHCP. Moreover, your LAN will see your internet traffic, especially on wireless.

Whoa, I suddenly realized after all these years that if you’re a grown man who lives in an attic, hangs out with high school students, has an ‘office’ in the bathroom, and is known for one syllable utterances, you’re not cool. You’re a loser.

Mac OS X 10.4.11: less tar

Thursday, December 6th, 2007

Although it seems late in the game to be adding new features to Tiger, Mac OS X 10.4.11 includes not only the high-profile Safari 3 update but also a few other interesting little additions. For instance, when you uncompress a .tar.gz file in Finder it no longer leaves behind a useless .tar that you have to manually trash. Neat!

Of course, it wouldn’t be Finder without some annoyance. The trade-off for un-cluttering your Desktop is up-cluttering your console.log:

BOMArchiveHelper[454] opened /Users/jeff/Desktop/.BAHEm7Xn/my-directory.tar

I’m not sure why they didn’t use tar xzf, which is approximately a billion times faster. Baby steps…

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.