Friday, August 28, 2009

Creating a Finder alias programmatically on Snow Leopard

My co-worker Dan posted a blog yesterday entitled "How to Create an Alias Programmatically". His example is 21 lines long, and is compatible with Leopard and earlier. It only works if the original item is a folder (though creating an alias to a file is not much different). The code messes with resource forks, and the C interface is generally difficult to read for someone who spends most of their time in Objective-C. The code is also not guaranteed to be 100% accurate, since Apple explicitly did not support programmatic alias creation in Leopard.

This is a great example of how Apple has improved the developer experience for OS X in Snow Leopard. Snow Leopard contains some new APIs for "bookmark" creation, which appears to be the new behind-the-scenes name for aliases. The following four lines of code create a functioning alias on Snow Leopard:

NSURL *src = [NSURL URLWithString:@"file:///Users/bjh/Desktop/temp.m"];
NSURL *dest = [NSURL URLWithString:@"file:///Users/bjh/Desktop/myalias"];

NSData *bookmarkData = [src bookmarkDataWithOptions:NSURLBookmarkCreationSuitableForBookmarkFile
includingResourceValuesForKeys:nil
relativeToURL:nil
error:NULL];
[NSURL writeBookmarkData:bookmarkData
toURL:dest
options:0
error:NULL];

Isn't that about a billion times easier?

I'm not sure what the relativeToURL: parameter is for yet; comments are welcome.

Thursday, July 9, 2009

100 Hour Board iPhone app v1.2 released

As of this previous Sunday, 100 Hour Board for iPhone was updated to version 1.2. This update includes support for iPhone OS 3.0, improves the rendering of quotes in Board posts, and teaches the app to remember where you were when you last closed it. There are also a couple minor user interface tweaks; notably, when posts download they should appear in the list with a smooth animation.

Go check it out! http://tinyurl.com/100hourapp (iTunes Music Store link)

Tuesday, June 16, 2009

Singleton Pattern in Cocoa

Peter Hosey recently tweeted:
From the “how NOT to implement a Cocoa singleton” dept: http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32

followed by:
If something over-releases an object, the bug is not in that object, and overriding methods in it to break retain/release is not fixing it.

The code sample in question is in the Apple documentation, and prescribes overriding the following methods for a singleton object: allocWithZone:, copyWithZone:, retain, release, autorelease, and retainCount. In addition, it also recommends implementing a sharedFloozit class method, so that users of your class are aware they're using a singleton. Go read that section, or you'll be lost for the rest of this post.

Really, I mean it. Go read that documentation. I'll be referring to it a lot.

That's a lot of methods to override just to implement a singleton. More to the point, a lot of it seems unnecessary. After all, as long as everyone else is doing their memory management correctly, retain counts shouldn't matter, right?

Wrong.

First, though, we need to get a definition straight. A singleton is a class of which there should only be one instance in any given process. There are actually very few singleton classes in the Cocoa framework including NSDocumentController and NSFontManager. [1] You cannot create more than one of these objects; if you try to call [[NSDocumentController alloc] init], you'll get back the exact same object as you do when you call [NSDocument sharedDocumentController], no matter how many times you call alloc and init. NSApplication is arguably a singleton as well; you can alloc another one, but you'll get an assertion failure when you call init.

Singletons are generally useful when initializing an object takes an inordinate amount of time. NSFontManager, for example, has to search several locations on the filesystem in order to do its job. You really don't want to be constantly initializing an NSFontManager. Further, it makes very little sense to have more than one instance of NSApplication within your application. Having one shared object for the whole system can simplify and optimize certain things. [2]

So that's a singleton. Only one instance allowed in the entire process.

There are other classes that have a +[SomethingClass sharedSomething] method or a +[SomethingClass defaultSomething] method. These methods provide access to an instance of the object intended to be shared by all users of the class, but do not prohibit the creation of another instance. Other Indeed, the Leopard Developer Release Notes specifically note that creating multiple instances of NSFileManager is possible and thread-safe, despite the existence of a globally-available object through +[NSFileManager defaultManager]. These are not singletons. If you're writing a class which provides a shared instance but doesn't not prohibit creation of other instances, then you should absolutely not override retain, release, autorelease, and retainCount, and should probably not override allocWithZone: either.

Most of the time, you don't need a singleton. Just the mention of a singleton is enough to get some people up in arms. But if you really truly need a singleton, then there's good reason for overriding the methods listed above.

Consider the following example:

MyFloozit *floozit1 = [[MyFloozit alloc] init];
[floozit1 doSomething];
[floozit1 release];

MyFloozit *floozit2 = [[MyFloozit alloc] init]; // MyFloozit is a singleton, so this should be the same object as floozit1
[floozit2 doSomething]; // CRASH HERE
[floozit2 release];


When floozit1 is set, a new MyFloozit is allocated, and a static MyFloozit pointer is set. When floozit1 is released, that static pointer is still pointing to the old instance. As a result, when we try to set floozit2 (or when anyone else tries to call [MyFloozit sharedFloozit]), we get back a pointer to that same instance. The one that has been dealloc-ed. Right there, despite following all the standard rules of memory management, you've crashed. The example might seem contrived, but if floozit1 and floozit2 are in separate methods (or separate threads calling the same method), this could be a very common scenario.

The point is, if you override allocWithZone: to force a class to be a singleton, then you must override release (and autorelease), or else anyone who believes they have ownership (after to calling init) will crash your program. Once you're disabling retain counting by overriding release, you might as well override retain and retainCount as well, to be consistent.

In the Apple Documentation linked above, there is a short paragraph right after the code showing the recommended way to override the various methods. It says:
Situations could arise where you want a singleton instance (created and controlled by the class factory method) but also have the ability to create other instances as needed through allocation and initialization. In these cases, you would not override allocWithZone: and the other methods following it as shown in Listing 2-15.
In this situation, I agree with Peter (and Apple) completely; don't override the memory management methods. I disagree with calling it a singleton, but whatever you want to call it, it's clear that you need those memory management methods.

If you think you need a singleton, think again. If you still think you need a singleton, bounce it off someone else to disabuse you of the notion. If you still need a singleton, then follow Apple's advice and override the memory management methods. Otherwise, you'll crash.

Of course, if you're using garbage collection, all retain count operations are no-ops, so none of this matters anyway. Feel free to go on your merry way, pitying the poor souls still living in a retain-counted world.

[1] The documentation referenced by Peter on "Creating a Singleton Instance" lists NSFileManager and NSWorkspace as examples of singletons, but this is incorrect. It is possible to create more than one instance of both these classes; Cocoa just happens to provide easy access to a shared instance which they suggest you use.

[2] It can also complicate a lot of things by introducing shared state. I'm not arguing for the use of the singleton pattern; I agree with Peter that most of the time, if you're using a singleton then you're "Doin It Rong". I'm simply arguing for Apple's implementation of the singleton pattern in the case where you really do need a singleton.

Tuesday, March 31, 2009

100 Hour Board released... a week ago

On March 19, my 100 Hour Board app was officially available in the App Store. There were nearly 100 downloads in the first two days, and that was before I'd even started doing any advertising.

Anyway, if you haven't downloaded it yet, it's available in the App Store here. (Link opens in iTunes.)

I've got a small update waiting for approval right now. It adds a popup alert when a search returns no results, so that users don't think it's just a slow search. Next on the feature list is read/unread tracking for daily posts.

Monday, March 9, 2009

100 Hour Board iPhone app coming soon

I'm a big fan of BYU's 100 Hour Board, an online Q&A forum that provides answers to nearly any question imaginable within 100 hours. I've been working with their webmasters over the last few weeks to get a web API to their content, and today I'm uploading an app that provides an iPhone-optimized interface to the 100 Hour Board. It's a fantastic interface on an awesome website, and I'm excited for it to go live.

I'll be sure to post as it's available.

Wednesday, February 11, 2009

Surakarta is now available

Surakarta for the iPhone/iPod Touch is now available for download. (Link opens in iTunes.) I haven't yet been able to find it directly by searching, but hopefully the App Store index will update shortly.

I'm currently working on version 1.1, where I'll add single-player support. In the meantime, download Surakarta and write a review!

UPDATE (6:04 pm): Sometime in the last 10 minutes, it showed up through search. Hooray!

Sunday, February 1, 2009

Surakarta 1.0 Released


Today I'm releasing Surakarta v1.0 for the iPhone (and iPod Touch). It's based on a traditional Javanese game and was included in "The Book of Classic Board Games" published by Klutz Press, under the name "Roundabouts." It currently only has two-player mode, but I plan to release an upgrade to support single-player play (against a computer opponent) soon.

I hope you enjoy the game! For feedback, please e-mail me.