Archive for the ‘Cocoa’ Category

Cocoa Blogs free, as in beer

Sunday, March 30th, 2008

Wait, beer is not free! Or so my bartender yelled last night as I ran out the door. (I also had one bourbon and one scotch.) The front page of Cocoa Blogs was already free, but the syndicated feed has only been free to contributors — kind of like Congress. Now Scott has decided (he is the decider) to make the feed available for free too. Indeed, new items are appearing in the feed. Remember, folks, you heard it here first. This is your Number 1 source for news, rumors, and outright lies.

My own long list of Cocoa blogs, including the Cocoa Blogs feed, can be downloaded for free from the “Favorite Feeds” link in the sidebar of my blog. The list is in convenient .opml format for import into your favorite free feed reader. There seem to be many choices nowadays. (Too bad that they all suck. Mine sucks the least, though.) The contents of my web site have been and always shall be free, as in loader. If I asked for contributions, that would just embolden the terrorists. Last call!

The beach is open

Saturday, March 1st, 2008

I know that March may be too early to think about going to the beach, but Code Beach does not require a tan, six-pack abs (so-called because beer drinkers usually have nice abs), or even swim trunks. No, it’s not for nudists, it’s for Mac developers! Nudist Mac developers, use your own discretion. The goal of Code Beach is to create a central repository where Mac developers can share pieces of useful code (like custom controls, formatters, categories) which is easier to search/use than forums or mailing lists. Brought to you by Briksoftware, Code Beach could use more traffic and more code. Don’t worry, there’s only one Shark, mostly harmless.

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.

Logging in Leopard

Sunday, January 6th, 2008

The release of Leopard has given third-party developers a lot to do: attempting to restore features lost from Tiger, for instance. (By the way, where is the second party, and why am I never invited?) My friend Rainer Brockerhoff has provided a way, or Quay, to display hierarchical popup menus in the Dock again. One of my most missed features in Leopard is using NSLog to spew output exclusively to Xcode’s console log. When you debug or run your app in Xcode on Tiger, you can put NSLog calls everywhere without worrying about polluting console.log. In my opinion, console.log is only for important messages and errors. I frequently ask users to consult it if they’re experiencing a problem with an app. Either that or the Oracle at Delphi.

Leopard dispenses completely with console.log, though there is a “Console Messages” database query in Console. Whereas on Tiger stdout and stderr standardly go to console.log, on Leopard they boldly go to system.log (as well as to the “Console Messages” query). On either version of Mac OS X, Xcode redirects stdout and stderr to its own console log, so they don’t appear in Console at all.

According to the documentation, NSLog sends a message to stderr. This is true for Tiger, and it’s also true for Leopard, but Leopard’s NSLog has the additional behavior of sending a message to system.log regardless of whether stderr is redirected. Thus, when you debug or run your app in Xcode (these may amount to the same thing in Xcode 3), messages from NSLog appear both in Xcode’s console log and in system.log! Curiously, there is no duplication of NSLog messages in system.log when stderr is not redirected.

If you prefer to keep your debug output out of system.log, the workaround for this new NSLog behavior is to abandon NSLog for debugging purposes on Leopard. :-( After much experimentation with asl, I realized that our old faithful printf would work. Since printf writes to stdout, its output is redirected by Xcode. Plus, when you’re debugging your app in Xcode you don’t really need NSLog to tell you the name of your app, the date, or your shoe size.

A limitation of printf is that it doesn’t handle the format specifier %@ for an Objective-C object. With Cocoa, therefore, we want an Objective-C wrapper around printf (like, um, NSLog). If you add the following code to your target’s .pch file, you’ll have an Objective-C debug logging function JJLog available throughout your target’s code. To enable logging in your app’s debug build, just add JJLOGGING to the GCC_PREPROCESSOR_DEFINITIONS setting (AKA “Preprocessor Macros”) in the debug build configuration.


#ifdef __OBJC__
	#import <Cocoa/Cocoa.h>
	#ifdef JJLOGGING
		#define JJLog(...) (void)printf("%s:%i %s: %s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, [[NSString stringWithFormat:__VA_ARGS__] UTF8String])
	#else
		#define JJLog(...)
	#endif
#endif

In your app’s release build, the debug function is a NOP that the compiler will almost certainly optimize out. This conditional code should not cause problems when using GCC_PRECOMPILE_PREFIX_HEADER, because Xcode already generates a separate precompiled prefix header for each build configuration. See the .pch.gch.hash-criteria files in /Library/Caches/com.apple.Xcode.###/SharedPrecompiledHeaders.

You can send gobs of gab to JJLog without repercussion or remorse. However, you’ll still want to use NSLog (sparingly, please) for runtime errors in your release build. Now to continue in the spirit of this post, I’ll redirect the epilogue to /dev/null.

How not to fix a build warning

Saturday, December 22nd, 2007

The Hollywood writers strike continues, and the desperation grows for alternative sources of entertainment. Fortunately, we programmers can find entertainment in our own sources. I’ve got some reality programming for you! The following snippet of code is taken from an actual CVS commit. (Yes, CVS. Don’t laugh. Do cry for me, Argentina.) This build warning ‘fix’ was made by some contractor for some project that I worked on at some point in time for some company. To protect the innocent and/or guilty, I won’t say who, what, when, or where. As for why, I wish I knew. Or maybe not.


NSEnumerator* fileEnum = [fileArray objectEnumerator];
NSDictionary* aDict = nil;
//Changed to Remove the Build Warnings
//while(aDict = [fileEnum nextObject])
while(aDict == [fileEnum nextObject])

Let this example serve as a lesson. Not for programmers — the one who wrote it is probably hopeless — but rather for managers. Please do not just hire the lowest bidder!

What ever happened to Cocoa Blogs?

Thursday, November 29th, 2007

It seems dead, though the clock is still ticking. (Countdown to extinction?) Anyway, I’ve just updated my own extensive list of cocoa blogs and other favorite feeds, which you can download from the Downloads section of my sidebar. FileMerge will reveal the changes in my all-important favor. Remember bloggers, X-Mas is coming soon, not to mention Y-Mas and Z-Mas. I’m keeping track of who’s naughty and nice — mostly by intercepting your wireless packets. If you’re nice, you’ll get a hot dog and a shake.

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!

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]